一文带你探索C++中类型转换的奥秘
作者:大神仙
const_cast(常量转换)
const_cast(常量转换)是一种类型转换运算符,用于修改类型的const或volatile属性。它通常用于将const变量转换为非常量类型,或者将volatile变量转换为非volatile类型。用法如下:
const_cast<new_type>(expression)
new_type是你要转换成的类型,expression是要被转换的表达式。
示例
int main() { const int a = 10; // int* pA = &a;//编译错误,不能将一个非常量指针指向一个常量对象。 int* pA = const_cast<int*>(&a); *pA = 20; //输入10 20 cout << "a:" << a << " *pA:" << *pA << endl; }
上面的代码定义一个 const int
类型的变量 a
,我们想要将其转换为 int 类型。直接用一个指针pA
指向这个a
时,由于a
本身是const类型,会导致编译错误。所以我们可以用 const_cast 把它做一个类型转换,去除 a
的 const 修饰符。然后修改 *pA
的值。
但是大家需要注意,最后的打印输出是a:10 *pA:20
。
即使你使用 const_cast 去除了指向 a
的指针的常量性, *pA = 20
修改了*pA
的值,实际上并不会影响 a
的输出,这是 C++ 语言的常量性规则所决定的。因为它是一个常量。在编译时,编译器将 a
的值直接插入到代码中,而不是通过指针 pA
访问 a。因此无论如何修改 *pA
,a
的值都保持不变。
const_cast 在某些情况下可以提高代码的可读性和减少冗余代码,但也存在一些缺点,例如增加代码的复杂度和容易产生歧义。
reinterpret_cast(重新解释转换)
reinterpret_cast
是一种非常危险类型转换。它不会检查我们所指向的内容,也不会检查我们这个类型的本身。它只是做了一个重新的解释,我们要在转换的过程当中,保证它转换前和转换后占用的内存大小应该是一致的。用于在不同类型的指针之间进行转换,用法如下:
reinterpret_cast<new_type>(expression)
示例
int Test() { std::cout << "Test function called." << std::endl; return 0; } int main() { //创建一个 FuncPtr 的函数指针类型。指向没有参数和返回值的函数。 typedef void(*FuncPtr) (); FuncPtr funcPtr; // funcPtr = &Test; funcPtr = reinterpret_cast<FuncPtr>(&Test); funcPtr(); return 0; }
上述代码 Test
函数的返回类型是 int,而 FuncPtr
类型是 void(*)()
,这两者的返回类型不匹配,因此不能将 Test
函数指针直接赋给 funcPtr
。可以通过reinterpret_cast
显式类型转换来解决这个问题,funcPtr = reinterpret_cast<FuncPtr>(&Test)
将 Test
函数的指针转换为 FuncPtr
类型的函数指针。
reinterpret_cast不进行类型检查非常灵活,可以在不同指针类型之间进行转换,包括将指针从对象类型转换为其他类型,或者在整数类型和指针之间进行转换。但是reinterpret_cast是非常危险的。因为他把这个检查的大部分工作交给程序员自身来管理的,可能导致转换不合理或不安全。
static_cast(静态转换)
static_cast(静态转换)可用于对基本类型进行转换,例如int *
转double *
,有继承关系的类对象和类指针之间转换。要注意的是,如果用static_cast
做一个继承关系的转换,安全问题需要由程序员自身来保障。
比如说动物,它有一个类叫鸭子,那么鸭子它一定是一种动物,所以它有动物的行为。但是动物未必都有鸭子的行为,所以如果我们把鸭子转换成动物,就是一个叫向上转换的过程。如果向下转换,那就是把动物转换成鸭子。但并不是所有的动物都是鸭子,所以这个地方就存在一些潜在的危险。如果我们使用static_cast
就不会发现这样的问题。但如果我们使用dynamic_cast
,就会帮我们检查出来这种错误。
示例
class Base { public: Base() : _i(0) {} virtual void T() { cout << "Base:T" << _i << endl; } private: int _i; }; class Derived : public Base { public: Derived() : _j(1) {} virtual void T() { cout << "Derived:T" << _j << endl; } private: int _j; }; void main() { int intValue = 5; double intValue2 = static_cast<double>(intValue); cout << intValue << " " << intValue2 << endl;//5 5 double dValue = 5.6; int dValue2 = static_cast<int>(dValue); cout << dValue << " " << dValue2 << endl;//5.6 5 丢失精度 Base base; Derived derived; Base* pBase; Derived* pDerived; // 父类--》子类 static_cast未做检查 可能导致运行时错误 pDerived = static_cast<Derived*>(&base); if (pDerived == NULL) { cout << "unsafe static cast from Base to Derived" << pDerived << endl; } }
上面的static_cast将base转化为Derived*,可能导致运行时错误。所以在进行类型转换时,要注意类型匹配规则。确保转换是安全的并且符合预期。例如,在进行向下转型时,需要确保子类对象可以存储父类对象。
dynamic_cast (动态转换)
dynamic_cast (动态转换)用于在类的继承层次之间进行类型转换,它既允许向上转型,也允许向下转型。向下转型会进行检测,如果转换失败则返回空指针或抛出std::bad_cast异常。用法如下:
dynamic_cast<new_type>(expression)
new_type 和 expression 必须同时是指针类型或者引用类型。
示例
class Base { public: Base() : _i(0) {} virtual void T() { cout << "Base:T" << _i << endl; } private: int _i; }; class Derived : public Base { public: Derived() : _j(1) {} virtual void T() { cout << "Derived:T" << _j << endl; } private: int _j; }; void main() { Base base; Derived derived; Base* pBase; Derived* pDerived; // 父类--》子类 pDerived输出NULL pDerived = dynamic_cast<Derived*>(&base); if (pDerived == NULL) { cout << "unsafe dynamic cast from Base to Derived" << endl; } }
上述代码创建了一个基类 Base
和一个派生类 Derived
,然后通过dynamic_cast
,将基类指针Base
转换为派生类指针 pDerived
。,dynamic_cast
返回 NULL
表示转换失败。
在动态类型转换中,只能将一个指向派生类对象的指针转换为基类指针,而不是反过来。这是因为派生类对象包含了基类的部分,但反之并不成立。
如果想安全地将指向基类对象的指针转换为指向派生类对象的指针,要确保对象实际上是派生类对象。
总结
const_cast
用于转换指针或引用,去掉类型的const属性;
reinterpret_cast
重新解释类型,既不检查指向的内容,也不检查指针类型本身;但要求转换前后的类型所占用内存大小一致,否则将引发编译时错误。
static_cast
用于基本类型转换,有继承关系类对象和类指针之间转换,由程序员来确保转换是安全的,它不会产生动态转换的类型安全检查的开销;
dynamic_cast
只能用于含有虚函数的类,必须用在多态体系中,用于类层次间的向上和向下转化;向下转化时,如果是非法的对于指针返回NULL;
到此这篇关于一文带你探索C++中类型转换的奥秘的文章就介绍到这了,更多相关C++类型转换内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!