C++中的显式类型转换
2016-06-22 20:39
513 查看
0. 前言
C++中类型的转换分位两种:隐式和显式转换;隐式转换发生在不经意间,比如int类型和float类型相加时,int类型会隐式转换为float类型,然后再相加运算。而今天主要介绍标准C++中的四个显式类型转换运算符:static_cast、dynamic_cast、const_cast和reinterpret_cast。本篇博文转自:果冻想,并做了细微的修改。注意:static_cast和dynamic_cast都可以对类间的类型进行转换,前者在向下转换时不在运行期进行类型检查,后者在运行期进行类型检查,较之安全,且后者可用于多态类型之间的转换,前者主要用于非多态类型间的转换。
1. static_cast
static_cast的转换格式:static_cast <new_type> (expression)
将expression转换为new_type类型,主要用于非多态类型之间的转换,不提供运行时的检查来确保转换的安全性。主要在以下几种场合中使用:
用于类层次结构中,基类和子类之间指针和引用的转换;
当进行上行转换,也就是把子类的指针或引用转换成父类表示,这种转换是安全的;
当进行下行转换,也就是把父类的指针或引用转换成子类表示,这种转换是不安全的,也需要程序员来保证;
用于基本数据类型之间的转换,如把int转换成char,把int转换成enum等等,这种转换的安全性需要程序员来保证;
把void指针转换成目标类型的指针,是及其不安全的;
注:static_cast不能转换掉expression的const、volatile和__unaligned属性。
2. dynamic_cast
dynamic_cast < new_type > (expression )
new_type - 必须是类的指针、类的引用或者void*
expression -将expression转换为new_type类型
注意:(1)如果转换成功,dynamic_cast返回new_type类型的值。
(2)如果转换失败且new_type是指针类型,则返回对于类型的空指针。
(3)如果转换失败且new_type是引用类型,则抛出异常std::bad_cast。
dynamic_cast主要应用于类层次之间的向上转换,向下转换或者交叉转换。向上转换时dynamic_cast和static_cast效果一样;向下转换时,dynamic_cast进行类型检查,比static_cast安全。在多态类型间转换主要使用dynamic_cast,因为多态类型提供了运行时的信息。
情形1:最常见的向上转换——B继承自A,B转换为A为向上转换,安全
情形2:多重继承之间的向上转换
类A派生出B,类B派生出类C,需要使用dynamic_cast
注意:向上转换默认被称为隐式转换,加不加dynamic_cast都可以,加了是显式转换;而上述两种情形的向上转换也可使用static_cast,效果是一样的,代码如下:
#include <iostream>
class A
{
};
class B : public A
{
};
void main(){
// 向上转换
// 隐式
A *a_ = new B();
// 显式
A *a = static_cast<A*>(new B());
A *_a = dynamic_cast<A*> (new B());
std::cin.get();
}
情形3:转换为void*
在类A必须包含虚函数,为什么呢?因为类中存在虚函数,就说明它有想让基类指针或引用指向派生类对象的情况,此时转换才有意义;由于运行时类型检查需要运行时类型信息,而这个信息存储在类的虚函数表中,只有定义了虚函数的类才有虚函数表。
情形4:向下转换
派生类赋值给基类对象的指针为向上转换,也是隐式转换,是安全的,加不加dynamic_cast或static_cast都可以;但是向下转换时,是不安全的,必须要显式转换且使用dynamic_cast。
#include <iostream>
class A{
public:
virtual void operation(){}
};
class B : public A{
public:
virtual void operation(){}
};
void main(){
A *a2A = new A();
B *b2A = dynamic_cast<B*>(a2A);// b2A points to a A not a B, now b2A is NULL
A *a2B = new B();
B *b2B = dynamic_cast<B*>(a2B);// ok: b2B actually points to a B
std::cin.get();
}
使用dynamic_cast需要注意的问题:
对于一些复杂的继承关系来说,使用dynamic_cast进行转换存在一些陷阱;比如,有如下结构的类继承关系:
![](https://oscdn.geek-share.com/Uploads/Images/Content/201908/30/cf40bb39fd9cd75796b67715fc67a841)
D类型可以安全的转换为B和C类型,但是D类型要是直接转换为A类型呢?下面以new_type为指针类型,如果转换失败,则出现NULL;同样的如果new_type为类对象的引用类型,那么如果转换失败,则会抛出异常;这里以指针类型为例:
#include <iostream>
// 注意:当发生多态公有继承时,通常需要设置抽象基类的析构函数为virtual,即虚函数
// 这里为理解方便,省略了这些内容,只保留核心内容
class A{
public:
virtual void operation() = 0;
};
class B : public A{
public:
virtual void operation(){
std::cout << "operation in B" << std::endl; }
};
class C : public A{
public:
virtual void operation(){
std::cout << "operation in C" << std::endl; }
};
class D : public B, public C{
public:
virtual void operation(){
std::cout << "operation in D" << std::endl; }
};
void main()
{
D *d = new D();
A *a = dynamic_cast<A*> (d);// ERROR 基类A不明确
a->operation();// 程序崩溃
std::cin.get();
}如果进行上面的直接转,你将会得到一个NULL的pA指针;这是因为,B和C都继承了A,并且都实现了虚函数operation,导致在进行转换时,无法进行抉择应该向哪个A进行转换。正确的做法是:
对于多重继承的情况,从派生类向父类进行转换时,需要特别注意;比如有如下情况:
![](https://oscdn.geek-share.com/Uploads/Images/Content/201908/30/542341bd2ae70f1d9885de16d8b4320e)
现在,你拥有一个A类型的指针,它指向E实例,如何获得B类型的指针,指向E实例呢?如果直接进行转的话,就会出现编译器出现分歧,不知道是走E->C->B,还是走E->D->B。对于这种情况,我们就必须先将A类型的指针进行下行转换,获得E类型的指针,然后,在指定一条正确的路线进行上行转换。
上面就是对于dynamic_cast转换的一些细节知识点,特别是对于多重继承的情况,在实际项目中,很容易出现问题。
3. const_cast
转换格式为:const_cast<new_type>(expression)
cons_cast用来将类型的const,volatile和_unaligned属性移除。常量指针/常量引用分别被转换为非常量指针/非常量引用,并仍然指向或者引用原来的对象。
注:你不能直接对非指针和非引用的变量使用const_cast操作符去直接移除它的const、volatile和__unaligned属性。
4. reinterpret_cast
reinterpret_cast转换格式为:reinterpret_cast<new_type >( expression )
允许将任何指针类型转换为其它的指针类型;听起来很强大,但是也很不靠谱。它主要用于将一种数据类型从一种类型转换为另一种类型。它可以将一个指针转换成一个整数,也可以将一个整数转换成一个指针,在实际开发中,先把一个指针转换成一个整数,在把该整数转换成原类型的指针,还可以得到原来的指针值;特别是开辟了系统全局的内存空间,需要在多个应用程序之间使用时,需要彼此共享,传递这个内存空间的指针时,就可以将指针转换成整数值,得到以后,再将整数值转换成指针,进行对应的操作。
C++中类型的转换分位两种:隐式和显式转换;隐式转换发生在不经意间,比如int类型和float类型相加时,int类型会隐式转换为float类型,然后再相加运算。而今天主要介绍标准C++中的四个显式类型转换运算符:static_cast、dynamic_cast、const_cast和reinterpret_cast。本篇博文转自:果冻想,并做了细微的修改。注意:static_cast和dynamic_cast都可以对类间的类型进行转换,前者在向下转换时不在运行期进行类型检查,后者在运行期进行类型检查,较之安全,且后者可用于多态类型之间的转换,前者主要用于非多态类型间的转换。
1. static_cast
static_cast的转换格式:static_cast <new_type> (expression)
将expression转换为new_type类型,主要用于非多态类型之间的转换,不提供运行时的检查来确保转换的安全性。主要在以下几种场合中使用:
用于类层次结构中,基类和子类之间指针和引用的转换;
当进行上行转换,也就是把子类的指针或引用转换成父类表示,这种转换是安全的;
当进行下行转换,也就是把父类的指针或引用转换成子类表示,这种转换是不安全的,也需要程序员来保证;
用于基本数据类型之间的转换,如把int转换成char,把int转换成enum等等,这种转换的安全性需要程序员来保证;
把void指针转换成目标类型的指针,是及其不安全的;
注:static_cast不能转换掉expression的const、volatile和__unaligned属性。
2. dynamic_cast
dynamic_cast < new_type > (expression )
new_type - 必须是类的指针、类的引用或者void*
expression -将expression转换为new_type类型
注意:(1)如果转换成功,dynamic_cast返回new_type类型的值。
(2)如果转换失败且new_type是指针类型,则返回对于类型的空指针。
(3)如果转换失败且new_type是引用类型,则抛出异常std::bad_cast。
dynamic_cast主要应用于类层次之间的向上转换,向下转换或者交叉转换。向上转换时dynamic_cast和static_cast效果一样;向下转换时,dynamic_cast进行类型检查,比static_cast安全。在多态类型间转换主要使用dynamic_cast,因为多态类型提供了运行时的信息。
情形1:最常见的向上转换——B继承自A,B转换为A为向上转换,安全
#include <iostream> class A { }; class B : public A { }; void main(){ B *b = new B(); A *a1 = b; // 隐式转换,常用 A *a2 = dynamic_cast<A*> (b);// 显示转换 std::cin.get(); }
情形2:多重继承之间的向上转换
类A派生出B,类B派生出类C,需要使用dynamic_cast
#include <iostream> class A { }; class B : public A { }; class C : public B { }; void main(){ C *c = new C(); // 使用dynamic_cast——显示转换 B *_b = dynamic_cast<B*> (c);// OK A *_a = dynamic_cast<A*> (c);// OK // 通常情况下向上转换可使用默认的隐式转换,向下转换时需要使用显式转换关键字 // 向上转换——隐式转换(常用) B *b = c; // OK A *a1 = b; // OK A *a2 = c;// OK std::cin.get(); }
注意:向上转换默认被称为隐式转换,加不加dynamic_cast都可以,加了是显式转换;而上述两种情形的向上转换也可使用static_cast,效果是一样的,代码如下:
#include <iostream>
class A
{
};
class B : public A
{
};
void main(){
// 向上转换
// 隐式
A *a_ = new B();
// 显式
A *a = static_cast<A*>(new B());
A *_a = dynamic_cast<A*> (new B());
std::cin.get();
}
情形3:转换为void*
#include <iostream> class A { public: virtual void operation(){} }; void main(){ A *a = new A(); void *pV = dynamic_cast<void *>(a); // pV points to an object of A void *pV2 = static_cast<void *>(a); std::cin.get(); }
在类A必须包含虚函数,为什么呢?因为类中存在虚函数,就说明它有想让基类指针或引用指向派生类对象的情况,此时转换才有意义;由于运行时类型检查需要运行时类型信息,而这个信息存储在类的虚函数表中,只有定义了虚函数的类才有虚函数表。
情形4:向下转换
派生类赋值给基类对象的指针为向上转换,也是隐式转换,是安全的,加不加dynamic_cast或static_cast都可以;但是向下转换时,是不安全的,必须要显式转换且使用dynamic_cast。
#include <iostream>
class A{
public:
virtual void operation(){}
};
class B : public A{
public:
virtual void operation(){}
};
void main(){
A *a2A = new A();
B *b2A = dynamic_cast<B*>(a2A);// b2A points to a A not a B, now b2A is NULL
A *a2B = new B();
B *b2B = dynamic_cast<B*>(a2B);// ok: b2B actually points to a B
std::cin.get();
}
使用dynamic_cast需要注意的问题:
对于一些复杂的继承关系来说,使用dynamic_cast进行转换存在一些陷阱;比如,有如下结构的类继承关系:
D类型可以安全的转换为B和C类型,但是D类型要是直接转换为A类型呢?下面以new_type为指针类型,如果转换失败,则出现NULL;同样的如果new_type为类对象的引用类型,那么如果转换失败,则会抛出异常;这里以指针类型为例:
#include <iostream>
// 注意:当发生多态公有继承时,通常需要设置抽象基类的析构函数为virtual,即虚函数
// 这里为理解方便,省略了这些内容,只保留核心内容
class A{
public:
virtual void operation() = 0;
};
class B : public A{
public:
virtual void operation(){
std::cout << "operation in B" << std::endl; }
};
class C : public A{
public:
virtual void operation(){
std::cout << "operation in C" << std::endl; }
};
class D : public B, public C{
public:
virtual void operation(){
std::cout << "operation in D" << std::endl; }
};
void main()
{
D *d = new D();
A *a = dynamic_cast<A*> (d);// ERROR 基类A不明确
a->operation();// 程序崩溃
std::cin.get();
}如果进行上面的直接转,你将会得到一个NULL的pA指针;这是因为,B和C都继承了A,并且都实现了虚函数operation,导致在进行转换时,无法进行抉择应该向哪个A进行转换。正确的做法是:
void main() { D *d = new D(); B *b = dynamic_cast<B*> (d); A *a = dynamic_cast<A*> (b); b->operation();// operation in D }
对于多重继承的情况,从派生类向父类进行转换时,需要特别注意;比如有如下情况:
现在,你拥有一个A类型的指针,它指向E实例,如何获得B类型的指针,指向E实例呢?如果直接进行转的话,就会出现编译器出现分歧,不知道是走E->C->B,还是走E->D->B。对于这种情况,我们就必须先将A类型的指针进行下行转换,获得E类型的指针,然后,在指定一条正确的路线进行上行转换。
上面就是对于dynamic_cast转换的一些细节知识点,特别是对于多重继承的情况,在实际项目中,很容易出现问题。
3. const_cast
转换格式为:const_cast<new_type>(expression)
cons_cast用来将类型的const,volatile和_unaligned属性移除。常量指针/常量引用分别被转换为非常量指针/非常量引用,并仍然指向或者引用原来的对象。
#include <iostream> class CA{ public: CA() : _iA(10){} int _iA; }; void main() { const CA *pA = new CA(); // pA->_iA = 100; // ERROR CA *pB = const_cast<CA *>(pA); pB->_iA = 100; // OK // Now the pA and the pB points to the same object std::cout << pA->_iA << ", " << pB->_iA << std::endl; const CA &a = *pA; // a._iA = 200;// error CA &b = const_cast<CA &> (a); b._iA = 200; // Now the a and the b reference to the same object std::cout << a._iA << ", " << b._iA << std::endl; std::cin.get(); }
注:你不能直接对非指针和非引用的变量使用const_cast操作符去直接移除它的const、volatile和__unaligned属性。
4. reinterpret_cast
reinterpret_cast转换格式为:reinterpret_cast<new_type >( expression )
允许将任何指针类型转换为其它的指针类型;听起来很强大,但是也很不靠谱。它主要用于将一种数据类型从一种类型转换为另一种类型。它可以将一个指针转换成一个整数,也可以将一个整数转换成一个指针,在实际开发中,先把一个指针转换成一个整数,在把该整数转换成原类型的指针,还可以得到原来的指针值;特别是开辟了系统全局的内存空间,需要在多个应用程序之间使用时,需要彼此共享,传递这个内存空间的指针时,就可以将指针转换成整数值,得到以后,再将整数值转换成指针,进行对应的操作。
相关文章推荐
- 【C++】Modbus通讯
- 双向循环链表
- 射击类飞机游戏
- java与C++的区别
- 求二叉树的宽度C语言版
- C++ 虚继承和虚函数同时存在的对象模型
- C++帮助手册man的安装和使用方法
- C++05、初识类和对象
- C语言 写的 表达式求值。
- C语言 写的 表达式求值。
- [c++]封装高精度运算
- c语言 ? : 条件表达式
- C++编译连接总结(一)--g++
- Qt写c++控制台中文乱码问题
- c语言----学生选课系统
- 二叉树的建立与递归遍历C语言版
- 3dsMax Material Import UnrealEngine4
- 线性表的顺序存储结构C语言版
- 线性表的链式存储C语言版
- Geekban极客班C++STL与泛型编程 第二周