参考文章:
1、【C++】四种强制类型转换
2、四种强制类型转换
C++ 四种强制类型转换
C语言中的强制类型转换(Type Cast)有显式和隐式两种,显式一般就是直接用小括号强制转换,TYPE b = (TYPE)a; 隐式就是直接 float b = 0.5; int a = b; 这样隐式截断(by the way 这样隐式的截断是向 0 取整的,我喜欢这么叫因为 0.9 会变成 0,1.9 变成 1,-0.9 变成 0,-1.9 变成 -1)。
C++对C兼容,所以上述方式的类型转换是可以的,但是有时候会有问题,所以推荐使用C++中的四个强制类型转换的关键字:
1、static_cast,2、const_cast,3、reinterpret_cast,4、dynamic_cast
静态转换、常量转换、重解释、动态转换
1)static_cast
这应该四种中是最常见的。用法为static_cast<type-id> (expression)
。
该运算符把 expression 转换为 type-id 类型,但没有运行时类型检查来保证转换的安全性。
主要用法如下:
(1)用于类层次结构中基类(父类)和派生类(子类)之间指针或引用的转换。
进行上行转换(把派生类的指针或引用转换成基类表示)是安全的;
进行下行转换(把基类指针或引用转换成派生类表示)时,由于没有动态类型检查,所以是不安全的。而且不推荐这种方式转换,如果有这种需要,最好使用dynamic_cast。
(2)用于基本数据类型之间的转换,如把int转换成char,把int转换成enum。这种转换的安全性也要开发人员来保证。
(3)把空指针转换成目标类型的空指针。
(4)把任何类型的表达式转换成void类型。
最常用的应该还是基本数据类型之间的转换,如下:
const auto a1 = 11; // int
const auto a2 = 4; // int
// C style
double res1 = (double)(a1) / (double)(a2); // 其实写一个 (double) 就行
cout << "res1 = " << res1 << endl; // res1 = 2.75
// C++ style
auto res2 = static_cast<double>(a1) / static_cast<double>(a2);
cout << "res2 = " << res2 << endl; // res2 = 2.75
cout << typeid(res2).name() << endl; // double
当然也有对类的操作:
class A{
public:
virtual void f(){
cout<<"A"<<endl;
}
void ff(){
cout<<"AA"<<endl;
}
};
class B:public A{
public:
virtual void f(){
cout<<"B"<<endl;
}
void ff(){
cout<<"BB"<<endl;
}
};
int main()
{
/*使用static_cast将A类型指针转换成B类型指针,向下转换*/
A *a=new B();
a->f();
a->ff();
B *b=static_cast<B*>(a);//向下转换,不推荐这种做法,尽量用dynamic_cast
b->f();
b->ff();
/*使用static_cast将B类型指针转换成A类型指针,向上转换,这种情况是安全的,就不演示了*/
}
运行结果:
[Running] cd "e:\source_vscode\demo\" && g++ demo.cpp -o demo && "e:\source_vscode\demo\"demo
B
AA
B
BB
[Done] exited with code=0 in 4.255 seconds
需要注意的是,static_cast 不能转换掉 expression 的 const、volitale 或者 __unaligned 属性。
2)const_cast
上边的 static_cast 不能将 const int* 转成 int*,const_cast 就可以,用法为``` const_cast<type-i> (expression) ```。如下面代码:
const int a = 10;
const int * p = &a;
*p = 20; // Compile error: Cannot assign readonly type 'int const'
int res1 = const_cast<int>(a); // Compile error: Cannot cast from 'int' to 'int' via const_cast
// only conversions to reference or pointer types are allowed
int* res2 = const_cast<int*>(p); // ok
也就是说,const_cast<>里边的内容必须是引用或者指针,就连把 int 转成 int 都不行。
总结来说,const_cast 通常是无奈之举,只是 C++ 提供了一种修改 const 变量的方式,但这种方式并没有什么实质性的用处,还是不用的好。const 的变量不要让它变。
注意:const_cast的作用并不是去修改一个const值,而是当你的值定义为一个const的时候,而你去调用函数,这个函数可能不是你自己写的,这个函数的参数必须是是一个非const值,那么通过const_cast强制转换未非const值,但是又不会改变const的值。
3)reinterpret_cast
reinterpret_cast 主要有三种强制转换用途:
1、改变指针或引用的类型
2、将指针或引用转换为一个足够长度的整形
3、将整型转换为指针或引用类型。
用法为reinterpret_cast <type-id> (expression)
。
type-id 必须是一个指针、引用、算术类型、函数针或者成员指针。它可以把一个指针转换成一个整数,也可以把一个整数转换成一个指针(先把一个指针转换成一个整数,在把该整数转换成原类型的指针,还可以得到原先的指针值)。
我们映射到的类型仅仅是为了故弄玄虚和其他目的,这是所有映射中最危险的。(这句话是C++编程思想中的原话)。因此, 你需要谨慎使用 reinterpret_cast。
4)dynamic_cast
用法为dynamic_cast<type-id> (expression)
。
几个特点如下:
(1)其他三种都是编译时完成的,dynamic_cast 是运行时处理的,运行时要进行类型检查。
(2)不能用于内置的基本数据类型的强制转换
(3)dynamic_cast 要求 <> 内所描述的目标类型必须为指针或引用。dynamic_cast 转换如果成功的话返回的是指向类的指针或引用,转换失败的话则会返回 nullptr
(4)在类的转换时,在类层次间进行上行转换(子类指针指向父类指针)时,dynamic_cast 和 static_cast 的效果是一样的。在进行下行转换(父类指针转化为子类指针)时,dynamic_cast 具有类型检查的功能,比 static_cast 更安全。 向下转换的成功与否还与将要转换的类型有关,即要转换的指针指向的对象的实际类型与转换以后的对象类型一定要相同,否则转换失败。在C++中,编译期的类型转换有可能会在运行时出现错误,特别是涉及到类对象的指针或引用操作时,更容易产生错误。Dynamic_cast操作符则可以在运行期对可能产生问题的类型转换进行测试。
(5)使用 dynamic_cast 进行转换的,基类中一定要有虚函数,否则编译不通过(类中存在虚函数,就说明它有想要让基类指针或引用指向派生类对象的情况,此时转换才有意义)。这是由于运行时类型检查需要运行时类型信息,而这个信息存储在类的虚函数表中,只有定义了虚函数的类才有虚函数表(C++中的虚函数基本原理这篇文章写得不错,https://blog.csdn.net/xiejingfa/article/details/50454819)。
class base {
public:
void print1() { cout << "in class base" << endl; }
};
class derived : public base {
public:
void print2() { cout << "in class derived" << endl; }
};
int main() {
derived *p, *q;
// p = new base; // Compilr Error: 无法从 "base * " 转换为 "derived * "
// Compile Error: Cannot cast from 'base*' to 'derived*' via dynamic_cast: expression type is not polymorphic(多态的)
// p = dynamic_cast<derived *>(new base);
q = static_cast<derived*>(new base); // ok, but not recommended
q->print1(); // in class base
q->print2(); // in class derived
}