C++学习笔记----9、发现继承的技巧(七)---- 转换(1)
我们来看下将一种数据类型转换为另一种数据类型中的一些令人迷惑的地方。
C++提供了五种类型转换:const_cast(),static_cast(),reinterpret_cast(),dynamic_cast()与std::bit_cast()。对于static_cast对于继承还有一些内容要讨论。现在你应该能够很熟练地书写自己的类,理解了类继承,是时候来详细探索一下这些转换了。
注意旧的C风格的比如(int)myFloat在C++中仍然可以用,并且在既有代码中还大量使用着。除了bit_cast之外C风格的转换涵盖了所有的转换,因此极易出错,因为并不是很明显地可以获得成功,可能会以非预期的结果结束。在新的代码中强烈推荐使用c++风格,因为更安全,语法更好,代码视觉更好。
1、static_cast()
可以使用static_cast()来执行语言直接支持的显式转换。例如,如果要写一个算术表达式,需要将int型转换为double型来避免整数除,使用static_cast()。在这个例子中,只要使用i的static_cast()就够了,因为只要使两个操作数中的一个为double就够了,确保c++执行浮点数的除。
int i { 3 };
int j { 4 };
double result { static_cast<double>(i) / j };
也可以使用static_cast()来执行显式转换也是允许的,使用用户定义的构造函数或转换函数。例如,如果类A有一个构造函数使用类B的对象,可以使用static_cast()将B对象转换为A对象。在大部分你想要这种行为的情况下,不用太多想,编译器会自动执行这种转换。
static_cast()的另一个用途是在继承层次结构中执行向下转换,示例如下:
class Base
{
public:
virtual ~Base() = default;
};
class Derived : public Base
{
public:
virtual ~Derived() = default;
};
int main()
{
Base* b{ nullptr };
Derived* d{ new Derived{} };
b = d; // Don't need a cast to go up the inheritance hierarchy.
d = static_cast<Derived*>(b); // Need a cast to go down the hierarchy.
Base base;
Derived derived;
Base& br{ derived };
Derived& dr{ static_cast<Derived&>(br) };
}
这种转换可以用于指针与引用。但对对象自身不可用。
注意使用static_cast()转换是不执行运行时类型检查的。允许将Base指针转换为指向Derived的指针,或者Base引用转换为Derived引用,即使运行时Base真的不是Derived。例如,下面的代码可以编译并且执行,但是使用指针d可能会造成潜在的灾难性后果,包括对象出界内存覆盖。
Base* b { new Base {} };
Derived* d { static_cast<Derived*>(b) };
要执行运行时类型检查的安全类型转换,要使用dynamic_cast(),我们后面会讨论。
static_cast()并不总是强大的。不能将一种类型转换为另一种不相关的类型的指针。如果没有转换构造函数,不能直接将一种类型的对象转换为另一种类型。不能将一个const类型转换为非const类型。不能将指针转换为int。本质上来讲,只能做根据c++类型规则说得通的转换。
2、reinterpret_cast()
reinterpret_cast()更强大一点儿,但伴随的是安全性降低,相比于static_cast()来讲。可以用其来执行在技术上c++类型规则不允许,但是在某些情况下对程序员来说是说得通的。例如,可以使用reinterpret_cast()来转换一种类型的引用到另一种类型的引用,即使类型是无关的。同样的,可以用其来发了日期指针类型到另外其它指针类型,即使在继承层次结构上是无关的。然而,将指针转换为void*可以隐式执行,而不需要显式转换。将void*转换来正确的类型指针,static_cast()就够了。void*指针只是指向内存中某个位置的指针。没有类型信息与void*指针相关。下面是一些例子:
class X {};
class Y {};
int main()
{
X x;
Y y;
X* xp{ &x };
Y* yp{ &y };
// Need reinterpret_cast for pointer conversion from unrelated classes
// static_cast doesn't work.
xp = reinterpret_cast<X*>(yp);
// No cast required for conversion from pointer to void*
void* p{ xp };
// static_cast is enough for pointer conversion from void*
xp = static_cast<X*>(p);
// Need reinterpret_cast for reference conversion from unrelated classes
// static_cast doesn't work.
X& xr{ x };
Y& yr{ reinterpret_cast<Y&>(x) };
}
reinterpret_cast()并不总是强大的;在什么可以转换为什么上有一些限制。我们就不详细讨论这些限制了,推荐审慎使用这些类型转换。
通常来说,要小心使用reinterpret_cast(),因为它不执行类型检查而允许做转换。
警告:可以使用reinterpret_cast()来对指针也整数进行相互转换。然而,只能将指针转换为能够存放该整数的大小的转换。例如,尝试使用reinterpret_cast()来转换一个64位的指针到一个32位的整数会造成编译时错误。