C++中的类型转换分两种:隐式类型转换和显式类型转换。
1. static_cast用法隐式转换,是标准的转换,很多时候是默认情况下由编译器进行转换;
显式转换,在C++中有四个类型的转换符:static_cast、dynamic_cast、const_cast、reinterpret_cast。
转换格式:
- 将expression转换为type_id类型,主要用于非多态类型之间的转换;
- static_cast转换,不能转换掉expression的const、volatile、和_unaligned属性
static_cast
(expression)
- 场景1,用于类层次结构中,基类和子类之间的指针或引用的转换
【1】上行转换,子类指针或引用转父类,这种转换是安全的。
多态类型之间转换:使用static_cast和使用dynamic_cast效果一样;
非多态类型之间转换:只能使用static_cast,使用dynamic_cast会编译报错;
//先定义一个父类A,和子类B class A { public: virtual void Func() { cout << "A::Func()" << endl; } }; class B : public A { public: void Func() override { cout << "B::Func()" << endl; } }; int main() { A* pa = new B; A* a = static_cast(pb);//ok,转换是安全的 B* pb = new B; A* a = static_cast(pb);//ok,转换是安全的 return 0; }
【2】下行转换,父类指针或引用转子类,这种转换是不安全的
int main() { //B* pb = new A; //error,编译报错,无法从A*转换为B* A* pa = new A; B* b = static_cast(pa); //ok,编译无误,b != nullptr,这是不安全的,没有做类型检查 return 0; }
- 场景2,用于基本数据类型之间的转换,如把int转成char,把int转换成enum等等,这种转换是不安全的
char转int,是安全的
int iVar1 = 10; char cVar1 = 'a'; iVar1 = static_cast(cVar1); cout << iVar1 << endl;
int转char,是不安全的,由ASSIC表得出,只有int[32,126]区间,转换才是安全的
int iVar2 = 10; char cVar2 = 'a'; cVar2 = static_cast(iVar2); cout << cVar2 << endl;
- 场景3,把void指针转换成目标类型,这种转换是及其不安全的,编译报错
int a = 10; void* pa = &a; int b = static_cast(pa); cout << b << endl; float c = static_cast (pa); cout << c << endl;
- 另外,static_cast转换,不能转换掉expression的const、volatile、和_unaligned属性
const int iVar = 110; int* pVar1 = static_cast2. dynamic_cast用法(&iVar);//编译报错 const int* pVar2 = static_cast (&iVar);//编译无误,通过
转换格式:
- 将expression转换为type-id类型,type-id必须是类的指针或引用或void*;
- 如果type-id是指针类型,那么expression也必须是一个指针;
- 如果type-id是引用类型,那么expression也必须是一个引用;
- dynamic_cast主要用于类层次间的上行转换和下行转换,还可以用于类之间的交叉转换;其中进行上行转换时,dynamic_cast和static_cast的效果一样;进行下行转换时,dynamic_cast具有类型检查功能,比static_cast更安全;
- 在多态类型之间转换主要使用dynamic_cast,因为类型提供了运行时信息。
dynamic_cast
(expression)
- 场景1,用于类层次结构中,基类和子类之间的指针或引用的转换
【1】上行转换(单继承),子类指针或引用转父类,这种转换是安全的。
多态类型之间转换:使用static_cast和使用dynamic_cast效果一样;
非多态类型之间转换:只能使用static_cast,使用dynamic_cast会编译报错;
class A { public: virtual void Func(){cout << "A::Func()" << endl;} }; class B: public A { public: virtual void Func() override{cout << "B::Func()" << endl;} }; int main() { B* pb = new B; A* a1 = dynamic_cast(pb); //OK a1->Func(); A* pa = new B; A* a2 = dynamic_cast(pa); //OK a2->Func(); return 0; }
如果Func为virtual,输出结果:
如果Func为非virtual,输出结果:
【2】上行转换(多重继承),子类指针或引用转父类,这种转换是安全的
class A { public: virtual void Func(){} }; class B: public A { public: virtual void Func() override{} }; class C : public B { public: virtual void Func() override{} }; int main() { C* c = new C; B* b = dynamic_cast(c); //OK A* a = dynamic_cast(c); //OK }
- 场景2,将类转换为void*,A类和B类中必须包含虚函数。
原因,类中存在虚函数,说明有想让基类指针或引用指向派生类对象的情况,此时转换才有意义;由于运行时类型检查需要运行时类型信息(该运行信息存储在类的虚函数表中),所以只有定义了虚函数的类才有虚函数表。
class A { public: virtual void Func() {} //注意关键字virtual // ...... }; class B : public A { public: virtual void Func() {} //注意关键字virtual // ...... }; int main() { A *pA = new A; B *pB = new B; void *pV = dynamic_cast(pA); // pV points to an object of A pV = dynamic_cast (pB); // pV points to an object of B system("pause"); return 0; }
去掉virtual关键字,会编译报错
- 场景3,如果expression类型是type-id的基类,使用dynamic_cast进行转换时,在运行时会检查expression是否真正的指向一个type-id类型的对象。如果是,则能进行正确的转换,得到真正的值;否则返回NULL;如果是引用,则在运行时就会抛出异常。
【1】下行转换(单继承),父类指针或引用转子类,会做类型安全检查,这种转换是安全的
class A { public: virtual void Func(){cout << "A::Func()" << endl;} }; class B: public A { public: virtual void Func() override{cout << "B::Func()" << endl;} }; int main() { //B* pb = new A; //error,编译报错,无法从父类A*转换为子类B* A* pa1 = new B; B* b1 = dynamic_cast(pa1); //ok,编译无误,b != nullptr if (nullptr != b1) { b1->Func(); } A* pa2 = new A; B* b2 = dynamic_cast(pa2); //ok,编译无误,b = nullptr if (nullptr != b2) { b2->Func(); } return 0; }
【2】 对于一些复杂继承关系,使用dynamic_cast进行转换是存在一些陷阱的
如下做法存在陷阱,
class A { virtual void Func() = 0; }; class B : public A { void Func() { cout << "B::Func()" << endl; } }; class C : public A { void Func() { cout << "C::Func()" << endl; }; }; class D : public B, public C { void Func() { cout << "D::Func()" << endl; } }; int main() { D *pD = new D; A *pA = dynamic_cast(pD); // pA = NULL //pA->Func(); system("pause"); return 0; }
正确的做法是,像多重继承关系这种情况,需要把子类逐步转换到父类
int main() { D *pD = new D; B *pB = dynamic_cast(pD); A *pA = dynamic_cast(pB); return 0; }3. const_cast用法
转换格式:const_cast用来将类型的const、volatile和_unaligned属性移除。
- 常量指针被转换为非常量指针,并且仍然指向原来的对象;
- 常量引用被转换为非常量引用,并且仍然引用原来的对象;
- 不能直接对非指针和非引用的变量使用const_cast操作符去直接移除它的const、volatile和__unaligned属性。
const_cast
(expression)
- 场景1,常量指针转非常量指针
class A { public: A() :m_iA(10) {} int m_iA; }; int main() { //常量指针转非常量指针 const A *pA = new A; //pA->m_iA = 100; //error,pA常量指针,指向的内容不能修改 cout << pA->m_iA << endl; A *pB = const_cast(pA); //去const属性 pB->m_iA = 100; //通过指针,修改值 //指针pA和pB指向同一个对象 cout << pA->m_iA << endl; cout << pB->m_iA << endl; return 0; }
- 场景2,指针常量转非指针常量
class A { public: A() :m_iA(10) {} int m_iA; }; int main() { //常量指针转非常量指针 A * const pA1 = new A; pA1->m_iA = 100; //ok,pA是指针常量,指向的内容可以修改 cout << pA1->m_iA << endl; A *pA2 = new A; //pA1 = pA2; //error,pA是指针常量,指向的地址不能修改 A *pB = const_cast(pA1); //去const属性 pB = pA2; //通过指针,修改值 pB->m_iA = 111; //指针pA和pB指向同一个对象 cout << pA1->m_iA << endl; cout << pA2->m_iA << endl; cout << pB->m_iA << endl; return 0; }
- 场景3,常量引用转非常量引用
int main() { const int &num = 10; //num = 100; //error,不能给常量赋值 int &num2 = const_cast(num); num2 = 100; return 0; }
- 场景4,不能给非常量指针和引用使用const_cast
int main() { const int num1 = 10; //int num2 = const_cast4. reinterpret_cast用法(num1); //error,编译报错,无法从“const int”转换为“int” return 0; }
转换格式:
reinterpret_cast
(expression)
reinterpret_cast后的尖括号中的type-id类型必须是一个指针、引用、算术类型、函数指针或者成员指针。它可以把一个指针转换成一个整数,也可以把一个整数转换成一个指针。