0%

C++强制类型转换

C++强制类型转换

强制类型转换

C 风格的强制类型转换 如下,不管什么类型的转换都可以使用使用下面的方式。

TypeName b = (TypeName)a;

C++也是支持C风格的强制转换,但是C风格的强制转换可能带来一些隐患,让一些问题难以察觉.所以C++提供了一组可以用在不同场合的强制转换的函数 包含:

  • const_cast
  • static_cast
  • dynamic_cast
  • reinterpret_cast

const_cast

主要用于 去掉 指针和引用的 的 const 属性。

  1. 常量指针 / 引用 被转化成非常量的指针,并且仍然指向原来的对象;
  2. const_cast一般用于修改指针。如const char *p形式
1
2
3
4
5
6
7
const char*c_ptr = "1234";
char *ptr = const_cast<char*>(c_ptr); // 通过const_cast<Ty> 去常量

// 以下的情况 对定义的常变量直接转换的方法 最好不要用 在不同编译器下可能会有不同的结果
const int c_val = 233; //声明为常量类型
int &use_val = const_cast<int&>(c_val); //使用去const 引用
int *ptr_val = const_cast<int*>(&c_val);//使用去const 指针

static_cast

  1. static_cast 作用和C语言风格强制转换的效果基本一样 这个和C相同 是指 ,可能会修改二进制的数据
    • 用于基本数据类型之间的转换(int char bool 等),如把int转换成char,把int转换成enum。这种转换的安全性需要开发者来维护。
  2. 用于类层次结构中基类(父类)和派生类(子类)之间指针或引用的转换。注意:进行上行转换(把派生类的指针或引用转换成基类表示)是安全的;进行下行转换(把基类指针或引用转换成派生类表示)时,由于没有动态类型检查,所以是不安全的
  3. 不能去掉原有的 属性 如 const voliate
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/* 常规的使用方法 */
float f_pi=3.141592f
int i_pi=static_cast<int>(f_pi); /// i_pi 的值为 3

/* class 的上下行转换 */
class Base{
// something
};
class Sub:public Base{
// something
}

// 上行 Sub -> Base
//编译通过,安全
Sub sub;
Base *base_ptr = static_cast<Base*>(&sub);
//下行 Base -> Sub
//编译通过,不安全
Base base;
Sub *sub_ptr = static_cast<Sub*>(&base);

dynamic_cast

c++ 多态性,例如在基类中定义一个虚函数,然后 子类 继承这个类并 实现虚函数,不同的子类可以有不同的虚函数实现。当我们将基类的指针指向子类的对象时,就会自动调用 对应子类的 虚函数的实现,这就体现出了多态性。

这个类型转换是 C 语言替代不了的 因为涉及到 C++ 的多态特性。对于C++ 中的类 的子类 和 基类 之间的指针或引用的转换,因该用dynamic 或者 static 转换。

  1. 当 将基类指针转换为 子类指针时,不能直接强转,也不能用static_cast。这是因为子类除了继承基类的变量和方法之外,还可能有自己新的扩充的定义,即 基类 < 子类,强转就可能出现指针越界。应该使用 dynamic_cast 来完成,因为dynamic 可以在程序运行过程中 对转换的可行性进行检查,如果无法完成,会返回空指针。
  2. dynamic_cast 的检查依赖于RTTI 实现,需要类中含有虚函数,没有虚函数 编译不通过。
  3. dynamic_cast 运行检查是有开销的 所以 如果是 子类 像 基类的 上行转换 用 staic_cast 比较快。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
#include<iostream>
using namespace std;

class Base{
public:
Base() {}
~Base() {}
void print() {
std::cout << "I'm Base" << endl;
}

virtual void i_am_virtual_foo() {}
};

class Sub: public Base{
public:
Sub() {}
~Sub() {}
void print() {
std::cout << "I'm Sub" << endl;
}

virtual void i_am_virtual_foo() {}
};
int main() {
cout << "Sub->Base" << endl;
Sub * sub = new Sub();
sub->print();
Base* sub2base = dynamic_cast<Base*>(sub);
if (sub2base != nullptr) {
sub2base->print();
}
cout << "<sub->base> sub2base val is: " << sub2base << endl;


cout << endl << "Base->Sub" << endl;
Base *base = new Base();
base->print();
Sub *base2sub = dynamic_cast<Sub*>(base);
if (base2sub != nullptr) {
base2sub->print();
}
cout <<"<base->sub> base2sub val is: "<< base2sub << endl;

delete sub;
delete base;
return 0;
}
/* vs2017 输出为下
Sub->Base
I'm Sub
I'm Base
<sub->base> sub2base val is: 00B9E080 // 注:这个地址是系统分配的,每次不一定一样

Base->Sub
I'm Base
<base->sub> base2sub val is: 00000000 // VS2017的C++编译器,对此类错误的转换赋值为nullptr
*/

reinterpret_cast

  • 强制类型转换符用来处理无关类型转换的 (例如指针 和 long long(64位机器指针是八字节) 的转换)
  • 为操作数的位模式提供较低层次的重新解释!但是他仅仅是重新解释了给出的对象的比特模型,并没有进行二进制的转换!
1
2
3
4
5
6
7
8
9
10
11
#include<iostream>
#include<cstdint>
using namespace std;
int main() {
int *ptr = new int(233);
uint64_t ptr_addr = reinterpret_cast<uint64_t>(ptr);
cout << "ptr 的地址: " << hex << ptr << endl
<< "ptr_addr 的值(hex): " << hex << ptr_addr << endl;
delete ptr;
return 0;
}