旧的C风格的强制类型转换仍然可以在c++中使用,并且仍在现有代码库中广泛使用。c风格的强制类型转换涵盖了所有4种c++强制类型转换,因此它更容易出错。因为要实现的目标不是很明确,并且最终可能带来意想不到的结果。所以在代码中建议使用c++的强制类型转换,因为他们更安全。
它可以使变量添加或者取消const属性。这是五个类型转换中唯一可以消除const属性的转换。
void func(char *str)
{
cout << str << endl;
}
void func2(const char *str)
{
func(const_cast<char *>(str));
}
此外,标准库还提供了名为std::as_const()的辅助方法。该方法定义在
中,该方法接收一个引用参数,返回他的const引用版本。基本上,as_const(obj)等价于const_cast
string str{"hello"};
const string &constStr{as_const(str)};
可以使用static_cast()执行语言直接支持的显示转换,例如,将int转换为double。
int i = 6, j = 7;
double k{static_cast<double>(i) / j};
还可以使用它执行显示转换。这是用户定义的构造函数或转换例程允许的。比如,如果类A有一个接受类B的构造函数,则可以使用static_cast()将B对象转换为A对象。但是,在大多数情况下,这种行为编译器会自动转换。
static_cast()的另外一个用途是在继承层次结构中执行向下强制转换。示例如下:
class Base
{
public:
Base() = default;
virtual ~Base() = default;
};
class Derived : public Base
{
public:
virtual ~Derived() = default;
};
int main()
{
Base *b{nullptr};
Derived *d{new Derived()};
b = d; // 向上转型,不需要类型转换
d = static_cast<Derived *>(b);
Base base;
Derived derived;
Base &br{derived};
Derived &dr{static_cast<Derived &>(br)};
return 0;
}
注意:使用static_cast()强制类型转换不会执行运行时的类型检查。所以在某些场景下会造成问题。例如:
Base *b{new Base()};
Derived* d{static_cast<Derived*>(b)};
使用指针d可能会导致潜在的风险,包括在对象边界之外进行内存覆盖。
static_cast
()并不是万能的。
与static_cast()
相比,reinterpret_cast()
的功能更加强大,但与此同时安全性也将变得更低。它可以执行c++
类型规则中不允许的某些强制类型转换,但是在某些情况下对于与开发者是有意义的。比如,即使类型不相关,也可以将一种类型的引用强制转换另一种类型的引用。同样,可以将指针类型强制转换为任何其他指针类型,即使他们与继承层次结构无关。这通常用于将指针强制转换为void*
类型的指针(可以隐士完成,不需要强制转换)。但是将void*
强制转换会正确的类型的指针需要reinterpret_cast()
。void*
类型的指针只是指向内存中的某个位置。没有类型信息与void*
类型的指针相关联。
class X{};
class Y{};
int main()
{
X x;
Y y;
X *xp{&x};
Y *yp(&y);
xp = reinterpret_cast<X *>(yp);
void *p{xp};
xp = reinterpret_cast<X *>(p);
X &xr{x};
Y &yr{reinterpret_cast<Y &>(xr)};
return 0;
}
reinterpret_cast()
并非万能,他对转化还有很多限制。通常应该谨慎使用,因为它允许在不执行任何类型检查的情况下进行转换。
dynamic_cast()提供了对继承层次结构中的强制转换的运行时检查。可以使用它强制转换指针或引用。dynamic_cast()在运行时检查底层对象的运行时类型信息。如果强制转换无效,dynamic_cast()将返回空指针(用于指针),或者抛出std::bad_cast异常(用于引用)
class Base
{
public:
Base() = default;
virtual ~Base() = default;
};
class Derived : public Base
{
public:
virtual ~Derived() = default;
};
int main()
{
Base *b;
Derived *d{new Derived()};
b = d;
d = dynamic_cast<Derived *>(b);
Base base;
Derived derived;
// 用于引用
Base &br{base};
try
{
Derived &dr{dynamic_cast<Derived &>(br)};
}
catch (const bad_cast &)
{
cout << "bad_cast!" << endl;
}
return 0;
}
注意:可以使用static_cast()或reinterpret_cast()在继承层次结构上执行相同的强制转换。与dynamic_cast()的区别在于,dynamic_cast()执行运行时(动态)类型检查,而另外两种,则执行强制转换,即使转换的类型是错误的。
运行时的类型信息存储在对象的虚表中。因此要使用dynamic_cast(),类中必须至少要有一个虚方法。如果类中没有虚表,就会导致错误。
c++20引入了std::bit_cast(),定义在
中。这是标准库唯一的强制类型转换,其他强制类型转换是c++语言本身的一部分。bit_cast()与reinterpret_cast()类似。但是它会创建一个指定目标类型的新对象,并按位从源对象复制到此新对象。它有效的将源对象的位解释为目标对象的位。bit_cast()要求源对象和目标对象的大小相同,并且两者都是可以复制的。示例:
float asFloat{1.23f};
auto asUint{bit_cast<unsigned int>(asFloat)};
if (bit_cast<float>(asUint) == asFloat)
{
cout << "success!" << endl;
}
bit_cast的一个用例是普通可复制类的二进制I/O。比如,可以将此类型的各个字节写入文件。当文件读回到内存时,可以使用bit_cast()正确的解释从文件中读取的字节。