C++中四种类型转换是:static_cast, dynamic_cast, const_cast, reinterpret_cast
const_cast
用于将const变量转为非const。const_cast中的类型必须是指针、引用或者指向对象类型成员的指针,主要是用来去掉const属性,当然也可以加上const属性。主要是用前者,后者很少用。核心:对地址的操作。
void const_cast_test()
{
class A
{
public:
A() : _num(0) {}
public:
int _num;
};
const int c_type = 100;
//auto var = const_cast<int>(c_type); //非指针、引用,开发环境报错
//类指针
const A a;
const A *pa = &a;
//pa->_num = 10; //开发环境报错
A *pa2 = const_cast<A *>(pa);
pa2->_num = 11;
std::cout << "pa->_num=" << pa->_num << "; pa2->_num=" << pa2->_num << std::endl;
//pa->_num=11; pa2->_num=11
//基本数据类型
const int i = 3;
int *p = const_cast<int *>(&i);
*p = 10; //success
std::cout << "i=" << i << "; *p=" << *p << std::endl;
//i=3; *p=10
{
volatile const int i = 3;
int *p = const_cast<int *>(&i);
*p = 10; //success
std::cout << "second i=" << i << "; *p=" << *p << std::endl;
//second i=10; *p=10
}
//加上const属性
int j = 10;
const int *k = const_cast<const int *>(&j); //一般可以直接写const int* k = &j;
if(0)
{
//对于本身定义为const的类型,应用const_cast去掉了const属性,然后再对这块内容进行write操作时,程序崩溃
const char* c_str = "hello world";
char* sz = const_cast<char*>(c_str);
sz[0] = 'H';
std::cout << "c_str =" << c_str<< "; sz =" << sz << std::endl;
//result---> Segmentation fault (core dumped)
}
}
static_cast
是最常用的转换,但是转换的时候不会检查类型来保证转换的安全性,因此安全性相对其他转换较低。需要注意的是:static_cast不能转换掉expression的const、volitale、或者__unaligned属性。用于各种隐式转换,比如非const转const,void*转指针等, static_cast能用于多态向上转化,如果向下转能成功但是不安全,结果未知。static_cast本质上是 传统c语言强制转换的替代品。核心:c语言强制转换。
void static_cast_test()
{
class base
{
};
class derived : public base
{
};
// 基本类型转换 float -> int
int i;
float f = 67.27f;
i = static_cast<int>(f);
//子类 -> 父类
derived d;
base b = static_cast<base>(d);
//父类 -> 子类
base bb;
derived *dd = static_cast<derived *>(&bb); //compile error
base *pB = new base;
derived *pD = static_cast<derived *>(pB); //编译通过,但是是******不安全的******(例如访问子类成员)
}
reinterpret_cast
即为重新解释,此标识符的意思即为数据的二进制形式重新解释,但是不改变其值。T必须是一个指针、引用、算术类型、函数指针或者成员指针。操作符用于将一种类型转换为另一种不同的类型,比如可以把一个整型转换为一个指针,或把一个指针转换为一个整型,因此使用该操作符的危险性较高,一般不应使用该操作符。
void reinterpret_cast_test()
{
class base
{
};
class derived : public base
{
};
class other
{
};
//1.不同类型之间的转换
//可以转,但是不安全
base* b1 = new derived;
other* o1 = reinterpret_cast<other*>(b1);//OK
//2.算术类型转指针
long l = 0;
base* b2 = reinterpret_cast<base*>(l);//OK
int i = 0;
b2 = reinterpret_cast<base*>(i);//OK
float f = 0.0;
//b2 = reinterpret_cast<base*>(f); //无法将浮点型变量进行reinterpret转换
char c = 'a';
b2 = reinterpret_cast<base*>(c);//OK
bool b = true;
b2 = reinterpret_cast<base*>(b);//OK
//3.指针转换为算术类型
base* b3 = new derived;
long l3 = reinterpret_cast<long>(b3);
}
dynamic_cast
一、简介:将一个基类对象指针(或引用)cast (抛)到继承类指针,dynamic_cast 会根据基类指针是否真正指向继承类指针来做相应处理。二、返回值:对指针进行dynamic_cast,失败返回null,成功返回正常cast后的对象指针;对引用进行dynamic_cast,失败抛出一个异常,成功返回正常cast后的对象引用。 三、注意:dynamic_cast在将父类cast到子类时,父类必须要有虚函数。即操作数中必须要有虚函数。四、原理:dynamic_cast<> 会用到RTTI技术。编译器都是通过vtable找到对象的RTTI信息的,如果基类没有虚方法,也就无法判断一个基类指针变量所指对象的真实类型。
void dynamic_cast_test()
{
class base
{
public:
virtual void print(){std::cout<<"base print"<<std::endl;}
};
class derived : public base
{
public:
void print(){std::cout<<"derived print"<<std::endl;}
};
class other
{
public:
virtual void print(){std::cout<<"other print"<<std::endl;}
};
//*************** 类无虚函数 ***************
//基类 -> 子类
base *pb0 = new base;
//derived *pd0 = dynamic_cast<derived *>(pb0); //如果pb0无虚函数时,IDE检查出错, 操作数必须包含 虚函数
//不同类型的转换
//会转换失败
other* o2 = new other;
derived* pd2 = dynamic_cast<derived*>(o2);
if(pd2 == nullptr){std::cout<<"pd2 == nullptr"<<std::endl;}
//基类 -> 子类
base *pb1 = new base;
derived *pd1 = dynamic_cast<derived *>(pb1); //失败,pd1 = NULL
if(pd1 == nullptr){std::cout<<"pd1 == nullptr"<<std::endl;}
//子类 -> 子类
base *pb3 = new derived;
derived *pd3 = dynamic_cast<derived *>(pb3); //成功
if(pd3 != nullptr){pd3->print();}
//子类 -> 基类
base *pb4 = new derived;
base *pd4 = dynamic_cast<base *>(pb4); //成功
if(pd4 != nullptr){pd4->print();}
}