C++面向对象整理(9)之C++的类型转换dynamic_cast、static_cast、const_cast
注:整理一些突然学到的C++知识,随时mark一下
例如:忘记的关键字用法,新关键字,新数据结构
C++ 的 类型转换
- C++面向对象整理(9)之C++的类型转换dynamic_cast、static_cast、const_cast
- 一、C++的类型转换dynamic_cast、static_cast、const_cast
- 二、类型转换举例
- 总结
提示:本文为 C++ 中 类型转换 的写法和举例
一、C++的类型转换dynamic_cast、static_cast、const_cast
类型转换是非常重要的操作,它允许我们改变变量的类型。C++提供了四种类型转换操作符:static_cast、dynamic_cast、const_cast和reinterpret_cast
。这些转换操作符在语法上相似,但它们在功能和使用场景上有所不同。
1、static_cast的静态类型转换
静态类型转换static_cast
是最常用的一种类型转换操作符,它主要用于非多态类型的转换。它可以执行以下几种转换:
基本数据类型之间的转换:例如,将int转换为double,或将char转换为int。
非多态类的上下转型:这包括非多态类之间的转换以及父子类之间的指针或引用转换。但需要注意的是,这种转换在运行时并不检查类型的安全性,因此在使用时需要特别小心。
空指针和空类型的转换:例如,将nullptr转换为任意类型的指针或引用。
枚举和整数之间的转换。
语法如下:
目标类型 转后变量 = static_cast<目标类型>(原变量);
2、 dynamic_cast的动态类型转换
动态类型转换dynamic_cast
主要用于类继承的安全转换,特别是涉及多态的情况。它用于继承中指针或引用类型之间的转换。
dynamic_cast还能用来侧向转换。
dynamic_cast 运行时会类型检查,它依赖于虚函数表(vtable)和 RTTI(运行时类型识别)机制来实现。
子类型到父类型的转换(子转父):这种转换是安全的,并且总是成功。
父类型到子类型的转换(父转子):这种转换只有在运行时对象确实是目标类型时才成功。如果转换失败,dynamic_cast会返回空指针(对于指针类型)或抛出异常(对于引用类型)。
语法如下:
目标类型 * variable = dynamic_cast<目标类型*>(原变量);
或者
目标类型 & variable = dynamic_cast<目标类型&>(原变量);
dynamic_cast在运行时进行类型检查,因此它比static_cast更安全,但性能开销也更大。
3、const_cast
const_cast
用于修改类型的const或volatile属性。只允许指针或者引用间的转换,且通常用于去除指针或引用的const属性,以便能够修改原本被声明为const的数据。
语法如下:
目标类型* variable = const_cast<目标类型*>(原变量);
或者
目标类型& variable = const_cast<目标类型&>(原变量);
使用const_cast时需要非常小心,因为它允许你修改原本不应该被修改的数据,这可能导致未定义的行为。
4、reinterpret_cast
reinterpret_cast
是 C++ 中最强大但也最危险的转换操作符之一。它允许程序员执行低级别的、几乎不检查类型的转换。reinterpret_cast 通常用于硬件寻址、位操作和将指针转换为整数等底层编程任务。由于它几乎不进行任何类型检查,因此使用时必须非常小心,以防止出现未定义行为,基本不推荐使用这个。
二、类型转换举例
1、 static_cast
例子:基本数据类型之间的转换
double d = 3.14;
int i = static_cast<int>(d); // 将double类型的d转换为int类型,i的值为3
例子:非多态类的上下转型
class Base {};
class Derived : public Base {};
Derived d;
Base* b = static_cast<Base*>(&d); // 将Derived类型的指针转换为Base类型的指针
2、dynamic_cast
例子:子类型到父类型的转换,又称向下转换(子类型到父类型的转换,安全)
向下转换是从基类指针或引用转换到派生类指针或引用。如果转换失败(即原指针或引用并不实际指向目标类型的对象),dynamic_cast 会返回空指针(对于指针类型)或抛出 std::bad_cast 异常(对于引用类型)。
class Base {
public:
virtual ~Base() {} // 需要虚析构函数来启用 RTTI
};
class Derived : public Base {};
int main() {
Base* basePtr = new Derived(); // 指向 Derived 对象的 Base 类型指针
Derived* derivedPtr = dynamic_cast<Derived*>(basePtr); // 安全转换
if (derivedPtr != nullptr) {
// 转换成功,derivedPtr 现在指向实际的 Derived 对象
delete derivedPtr; // 通过派生类指针释放内存
} else {
// 转换失败,不会执行到这里
}
return 0;
}
例子:父类型到子类型的转换(父转子,不安全)
Base* basePtr = new Derived(); // 假设basePtr实际上指向Derived对象
Derived* derivedPtr = dynamic_cast<Derived*>(basePtr);
if (derivedPtr != nullptr) {
// 转换成功,derivedPtr现在指向实际的Derived对象
} else {
// 转换失败,basePtr不指向Derived对象
}
侧向转换(兄弟类之间的转换)
侧向转换是在具有共同基类的两个派生类之间进行转换。这种转换同样依赖于 RTTI,并且同样会在运行时检查类型是否匹配。
class Base {
public:
virtual ~Base() {} // 虚析构函数
};
class Derived1 : public Base {};
class Derived2 : public Base {};
int main() {
Base* basePtr = new Derived1(); // 指向 Derived1 对象的 Base 类型指针
Derived2* derived2Ptr = dynamic_cast<Derived2*>(basePtr); // 尝试侧向转换
if (derived2Ptr == nullptr) {
// 转换失败,basePtr 不指向 Derived2 对象
} else {
// 转换成功,但这种情况通常不会发生,除非有多重继承的复杂情况
}
delete basePtr; // 通过基类指针释放内存
return 0;
}
注意事项
dynamic_cast 只能用于含有虚函数的类,即多态类。
dynamic_cast 主要用于处理指针和引用类型。
对于指针类型的转换,如果转换失败,dynamic_cast 会返回空指针。
对于引用类型的转换,如果转换失败,会抛出 std::bad_cast 异常。
使用 dynamic_cast 时需要确保对象实际上是指向目标类型的,否则转换会失败。
dynamic_cast 在性能上比 static_cast 要慢一些,因为它涉及到运行时类型检查。
3、const_cast
例子:去除const属性
const int a = 10;
int* p = const_cast<int*>(&a); // 去除const属性,但修改*p是未定义行为
// 注意:尽管上面的代码可以编译通过,但尝试修改*p的值是危险的,并且可能导致运行时错误。
在真实编程中,应该尽量避免使用const_cast来修改const对象,因为这破坏了const的语义,并可能导致不可预见的错误。const_cast的正确使用场景通常是在你知道某个const对象实际上不需要const属性,并且你有理由这么做的情况下。
4、reinterpret_cast
reinterpret_cast
及其不安全,不推荐使用重解释转换。
例子:指针与整数之间的转换
int x = 42;
int* ptr = &x;
// 将指针转换为整数
intptr_t intPtrValue = reinterpret_cast<intptr_t>(ptr);
// 将整数转换回指针
int* ptrFromInt = reinterpret_cast<int*>(intPtrValue);
// 注意:此时ptrFromInt指向的地址与ptr相同,但转换过程并未检查类型的匹配性
例子:不同类型指针之间的转换
float* floatPtr = new float(3.14f);
char* charPtr = reinterpret_cast<char*>(floatPtr);
// 此时charPtr指向floatPtr所指向的内存地址,但是解释内存内容的方式不同
// 这样的转换通常用于底层内存操作,并且需要程序员完全了解所涉及的类型和数据布局
在实际编程中,应该尽量避免使用 reinterpret_cast,除非你非常清楚它的含义和潜在风险,并且没有其他更安全的转换方法可用。如果可能的话,应该优先使用 static_cast、dynamic_cast 和 const_cast,因为它们提供了更安全的类型转换机制。
总结
static_cast
用于执行非多态类型的转换,包括基本内置数据类型之间的转换和类之间的转换。
dynamic_cast
主要用于类继承体系中的安全转换,特别是在涉及多态的情况下,不能用于内置数据类。
const_cast
用于修改类型(只能指针或者应用)的const或volatile属性。