0%

C++ 类型转换 static_cast、const_cast、dynamic_cast、reinterpret_cast

C 语言中的强制类型转换主要用于基础数据类型之间的转换,语法格式为:

1
2
(type_id) expression
type_id (expression)

C++ 新增了四中强制类型转换:static_cast、const_cast、dynamic_cast、reinterpret_cast
主要用于继承关系类间的强制转换,语法格式为:

1
2
3
4
5
6
7
static_cast<type_id> (expression)
const_cast<type_id> (expression)
dynamic_cast<type_id> (expression)
reinterpret_cast<type_id> (expression)

type_id: 目标数据类型
expression: 原始数据类型变量或者表达式
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
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
//示例所用class
class None
{
public:
None(){}
~None(){}

void ShowInfo()
{
PRINT_INFO("Class None," << __func__);
}

};

class Base
{
public:
Base(){}
~Base(){}

void ShowInfo()
{
PRINT_INFO("Class Base," << __func__);
}
};

class Derived : public Base
{
public:
Derived(){}
~Derived(){}

void ShowInfo()
{
PRINT_INFO("Class Derived," << __func__);
}

void DerivedFunc()
{
PRINT_INFO("Class Derived," << __func__);
}
};

class Derived2 : public Derived
{
public:
Derived2(){}
~Derived2(){}

void ShowInfo()
{
PRINT_INFO("Class Derived2," << __func__);
}

};

class Base1
{
public:
Base1(){}
~Base1(){}

virtual void ShowInfo()
{
PRINT_INFO("Class Base1," << __func__);
}
};

class Derived1 : public Base1
{
public:
Derived1(){}
~Derived1(){}

void ShowInfo()
{
PRINT_INFO("Class Derived1," << __func__);
}
};

static_cast

编译器在编译期处理
static_cast (expression)
type_id 和 expression必须是指针、引用、算术类型或枚举类型

static_cast 相当于传统的C语言里的强制转换,该运算符把 expression 转换为 type_id 类型,用来强迫隐式转换,但没有运行时类型检查来保证转换的安全性

只能在有联系的指针类型间进行转换,不能转换成继承体系外的一种类型

主要有如下几种用法:

  1. 用于类层次结构中基类(父类)和派生类(子类)之间指针或引用的转换
    进行上行转换(把派生类的指针或引用转换成基类指针或引用)是安全的
    进行下行转换(把基类的指针或引用转换成派生类指针或引用)时,由于没有动态类型检查,所以是不安全的
  2. 用于基本数据类型之间的转换,如把 int 转换成 char,把 int 转换成 enum 。这种转换的安全性也要开发人员来保证。
  3. 把void指针转换成目标类型的指针(不安全)
  4. 把任何类型的表达式转换成void类型
    PS: static_cast 不能转换掉 expression 的const、volitale、或者 __unaligned 属性
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
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
//Basic types 基本类型转换
float a = 11.1;
int b = static_cast<int>(a); //char -> int
PRINT_INFO("TestStaticCast, before cast type(float) a = " << a);
PRINT_INFO("TestStaticCast, after cast type(int) b = " << b);

int* c = new int;
void* d = static_cast<void* >(c); //char* -> void*
PRINT_INFO("TestStaticCast, pointer address = " << c);
PRINT_INFO("TestStaticCast, pointer address = " << d);
delete c;
c = NULL;

int e = 5;
const int f = static_cast<const int>(e); //int -> const int
PRINT_INFO("TestStaticCast, before cast type(int) e = " << e);
PRINT_INFO("TestStaticCast, after cast type(const int) f = " << f);
e = 6;
//Compile failed.
//f = 6; //error: assignment of read-only variable ‘f’

int* h = new int(7);
const int* i = static_cast<const int* >(h);//int* -> const int*
PRINT_INFO("TestStaticCast, before cast type(int*) h = " << *h);
PRINT_INFO("TestStaticCast, after cast type(const int*) i = " << *i);
*h = 8;
//Compile failed.
//*i = 9; //error: assignment of read-only location ‘* i’
PRINT_INFO("TestStaticCast, before cast type(int*) h = " << *h);
PRINT_INFO("TestStaticCast, after cast type(const int*) i = " << *i);
delete h;
h = NULL;


const int *g = new int(6);
//Compile failed.
//int* h = static_cast<int* >(g); //error: invalid static_cast from type ‘const int*’ to type ‘int*’
//static_cast 不能去掉const属性
delete g;
g = NULL;

char j = '1';
int* k = (int*)&j;
printf("TestStaticCast, char j address = %p\n", &j);
PRINT_INFO("TestStaticCast, int* k address = " << k);
PRINT_INFO("TestStaticCast, char j = " << j);
PRINT_INFO("TestStaticCast, k = " << *k);
//*k = 5; // run-time error: stack corruption
PRINT_INFO("TestStaticCast, char j = " << j);
PRINT_INFO("TestStaticCast, k = " << *k);

/**
* compile-time error:
* error: invalid static_cast from type ‘char*’ to type ‘int*’
*/
//int* l = static_cast<int*>(&j); //char* -> int*

//class 上行转换
{
//ShowInfo不是虚函数
Derived derived;
derived.ShowInfo();

Base* pBase = static_cast<Base* >(&derived); //上行转换,安全
if(pBase != NULL)
{
pBase->ShowInfo(); //Class Base,ShowInfo
}

//ShowInfo是虚函数
Derived1 derived1;
derived1.ShowInfo();

Base1* pBase1 = static_cast<Base1* >(&derived1); //上行转换,安全
if(pBase1 != NULL)
{
pBase1->ShowInfo(); //Class Derived1,ShowInfo
}
}

PRINT_INFO("--------------static_cast up-conversion end------------------");

//class 下行转换
{
//ShowInfo不是虚函数
Base base;
base.ShowInfo();

//&base 是基类指针
Derived* pDerived = static_cast<Derived* >(&base);//下行转换,不安全
if(pDerived != NULL)
{
pDerived->ShowInfo(); //Class Derived,ShowInfo
}

//ShowInfo是虚函数
Base1 base1;
base1.ShowInfo();

//&base1 是基类指针
Derived1* pDerived1 = static_cast<Derived1* >(&base1);//下行转换,不安全
if(pDerived1 != NULL)
{
pDerived1->ShowInfo(); //Class Base1,ShowInfo
}
}

PRINT_INFO("--------------static_cast down-conversion end------------------");

//转换为void*
{
//ShowInfo不是虚函数
Base base;
base.ShowInfo();

//&base 是基类指针
void* p1 = static_cast<void* >(&base);
if (p1 != NULL)
{
PRINT_INFO("p1 is not null," << __func__ << ":" << __LINE__);
}

//ShowInfo是虚函数
Base1 base1;
base1.ShowInfo();

//&base1 是基类指针
void* p2 = static_cast<void* >(&base1);
if (p2 != NULL)
{
PRINT_INFO("p2 is not null," << __func__ << ":" << __LINE__);
}

Derived derived;
derived.ShowInfo();

void* p3 = static_cast<void* >(&derived);
if (p3 != NULL)
{
PRINT_INFO("p3 is not null," << __func__ << ":" << __LINE__);
}

Derived1 derived1;
derived1.ShowInfo();

void* p4 = static_cast<void* >(&derived1);
if (p4 != NULL)
{
PRINT_INFO("p4 is not null," << __func__ << ":" << __LINE__);
}
}

PRINT_INFO("--------------static_cast void* end------------------");

//class 两类无关系
{
Base base;
base.ShowInfo();

//Compile failed.
//None* pNone = static_cast<None* >(&base);//error: invalid static_cast from type ‘Base*’ to type ‘None*’
}

PRINT_INFO("————————TestStaticCast End————————");

const_cast

编译器在编译期处理
const_cast (expression)
type_id 和 expression必须是相同的类型

const_cast 用来将类型的 const、volatile 和 __unaligned 属性移除
常量指针被转化成非常量指针,并且仍然指向原来的对象;
常量引用被转换成非常量引用,并且仍然指向原来的对象;

const_cast操作不能在不同的种类间转换;
相反,它仅仅把一个它作用的表达式转换成常量;
它可以使一个本来不是const类型的数据转换成const类型的,或者把const属性去掉。

PS: 不能直接对非指针和非引用的变量使用 const_cast 操作符去直接移除它的 const、volatile 和 __unaligned 属性。

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
class A
{
public:
A() : m_i(0){}
~A(){}

int m_i;
};

// const_cast用来将类型的const、volatile和__unaligned属性移除
const A* ca = new A;
//error: assignment of member ‘TestConstCast()::A::m_i’ in read-only object
//ca->m_i = 9;
A* a = NULL;
// error: invalid conversion from ‘const TestConstCast()::A*’ to ‘TestConstCast()::A*’ [-fpermissive]
//a = ca;
a = const_cast<A* >(ca);
a->m_i = 10;

PRINT_INFO("ca->m_i = " << ca->m_i);
PRINT_INFO("a->m_i = " << a->m_i);

const A& cra = *ca;
//error: assignment of member ‘TestConstCast()::A::m_i’ in read-only object
//cra.m_i = 9;
A& ra = const_cast<A& >(cra);
ra.m_i = 11;

PRINT_INFO("cra.m_i = " << cra.m_i);
PRINT_INFO("ra.m_i = " << ra.m_i);

// 不能直接对非指针和非引用的变量使用const_cast操作符去直接移除它的const、volatile和__unaligned属性。
int b = 12;
//int d = const_cast<int>(b);
int& d = const_cast<int&>(b);
d = 13;
PRINT_INFO("b = " << b << ",d = " << d);

const char* pc = "dzw";
// 表面上通过编译去掉了const性,但是操作其地址时系统依然不允许这么做
char* c = const_cast<char* >(pc);
//c[0] = 'a'; //run-time error; Segmentation fault (core dumped)
PRINT_INFO("pc = " << pc);

dynamic_cast

在运行期,会检查这个转换是否可能。

dynamic_cast (expression)
type_id 必须是类的指针、类的引用或者是void *
expression 必须是决定一个指针或引用的表达式

dynamic_cast 仅能应用于指针或者引用,不支持内置数据类型
为了让 dynamic_cast 能正常工作,必须让编译器支持运行期类型信息(RTTI)。

dynamic_cast 主要用于类层次间的上行转换和下行转换,还可以用于类之间的交叉转换。在类层次间进行上行转换时,dynamic_cast 和 static_cast 的效果是一样的;在进行下行转换时,dynamic_cast 具有类型检查的功能,比 static_cast 更安全。在多态类型之间的转换主要使用 dynamic_cast,因为类型提供了运行时信息。

如果转换为指针类型,失败的话返回NULL;如果转换为引用,则在运行时会抛出异常。

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
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
//class 上行转换
{
{
//ShowInfo不是虚函数
Derived derived;
derived.ShowInfo();

Base* pBase = dynamic_cast<Base* >(&derived); //上行转换,安全
if(pBase != NULL)
{
pBase->ShowInfo();
}

//ShowInfo不是虚函数
Derived1 derived1;
derived1.ShowInfo();

Base1* pBase1 = dynamic_cast<Base1* >(&derived1); //上行转换,安全
if(pBase1 != NULL)
{
pBase1->ShowInfo();
}
}

PRINT_INFO("--------------------------------");

//多重继承
{
//Derived2 -> Derived -> Base
Derived2 derived2;
derived2.ShowInfo();

Derived* pDerived = dynamic_cast<Derived* >(&derived2); //上行转换,安全
if(pDerived != NULL)
{
pDerived->ShowInfo();
}

Base* pBase = dynamic_cast<Base* >(&derived2); //上行转换,安全
if(pBase != NULL)
{
pBase->ShowInfo();
}
}

PRINT_INFO("--------------------------------");

//引用
{
Derived derived;
Derived& refDerived = derived;
refDerived.ShowInfo();

try
{
Base& base = dynamic_cast<Base& >(refDerived); //上行转换
base.ShowInfo();
}
catch(std::bad_cast)
{
PRINT_ERROR("dynamic_cast failed.");
}
}

PRINT_INFO("--------------------------------");
}

PRINT_INFO("--------------dynamic_cast up-conversion end------------------");

//转换为void* (class 中必须包含virtual函数)
{
Derived derived;
derived.ShowInfo();

void * pp = &derived;
Base* ppp = (Base*)pp;
ppp->ShowInfo();
PRINT_INFO("++++++++++++++++++++");
//Compile failed.
//error: cannot dynamic_cast ‘& derived’ (of type ‘class Derived*’) to type ‘void*’ (source type is not polymorphic)
//void* pBase = dynamic_cast<void* >(&derived);

/*
在类Base1和类Derived1中必须包含虚函数,为什么呢?
因为类中存在虚函数,就说明它有想让基类指针或引用指向派生类对象的情况,此时转换才有意义;
由于运行时类型检查需要运行时类型信息,而这个信息存储在类的虚函数表中,只有定义了虚函数的类才有虚函数表。
*/
Base1 base;
base.ShowInfo();

Derived1 derived1;
derived1.ShowInfo();

void* p = dynamic_cast<void* >(&derived1);
p = dynamic_cast<void* >(&base);

}

PRINT_INFO("--------------dynamic_cast void* end------------------");

//class 下行转换
{
{
//无虚函数
Base base;
base.ShowInfo();

/*
error: cannot dynamic_cast ‘& base’ (of type ‘class Base*’) to type ‘class Derived*’ (source type is not polymorphic)
*/
/*
由于运行时类型检查需要运行时类型信息,而这个信息存储在类的虚函数表,
只有定义了虚函数的类才有虚函数表,没有定义虚函数的类是没有虚函数表的。
*/
/*
Derived* pDerived = dynamic_cast<Derived* >(&base); //下行转换,安全
if(pDerived != NULL)
{
pDerived->ShowInfo();
}
else
{
PRINT_INFO("pDerived is NULL");
}*/

//有虚函数
Base1 base1;
base1.ShowInfo();

Derived1* pDerived1 = dynamic_cast<Derived1* >(&base1); //下行转换,安全
if(pDerived1 != NULL)
{
pDerived1->ShowInfo();
}
else
{
PRINT_INFO("pDerived1 is NULL," << __func__ << ":" << __LINE__);
}
}

{
Base1* pBase1 = new Derived1;
Base1* pBase2 = new Base1;

Derived1* pDerived1 = dynamic_cast<Derived1* >(pBase1); //下行转换,安全
if (pDerived1 != NULL)
{
pDerived1->ShowInfo();
}
else
{
PRINT_INFO("pDerived1 is NULL," << __func__ << ":" << __LINE__);
}

Derived1* pDerived2 = dynamic_cast<Derived1* >(pBase2); //下行转换,安全
if (pDerived2 != NULL)
{
pDerived2->ShowInfo();
}
else
{
PRINT_INFO("pDerived2 is NULL," << __func__ << ":" << __LINE__);
}

delete pBase1;
delete pBase2;
}
}

PRINT_INFO("--------------dynamic_cast down-conversion end------------------");

//复杂继承关系
{
// 菱形继承
// A
// / \
// B C
// \ /
// D

class A
{
public:
virtual ~A(){}
};

class B : public A
{
public:
virtual ~B(){}
};

class C : public A
{
public:
virtual ~C(){}

};

class D : public B, public C
{
public:
virtual ~D(){}
};

D* pd = new D;
//error: ‘TestDynamicCast()::A’ is an ambiguous base of ‘TestDynamicCast()::D’
//class B,class C在继承A时使用virtual,即虚继承就可以!!!!
//A* pa = dynamic_cast<A* >(pd);
/*
if (pa != NULL)
{
PRINT_INFO("pa is not null");
}
else
{
PRINT_INFO("pa is null");
}*/

B* pb = dynamic_cast<B* >(pd);
if (pb != NULL)
{
A* pa = dynamic_cast<A* >(pb);
if (pa != NULL)
{
PRINT_INFO("pa is not null," << __func__ << ":" << __LINE__);
}
}

delete pd;
}

reinterpret_cast

编译器在编译期处理
reinterpret_cast (expression)
任何指针都可以转换成其它类型的指针,type_id必须是一个指针、引用、算术类型、指向函数的指针或指向一个类成员的指针。

static_cast 和 reinterpret_cast 操作符修改了操作数类型。它们不是互逆的;
static_cast 在编译时使用类型信息执行转换,在转换执行必要的检测(诸如指针越界计算, 类型检查). 其操作数相对是安全的。
另一方面;reinterpret_cast 是 C++ 里的强制类型转换符,操作符修改了操作数类型,但仅仅是重新解释了给出的对象的比特模型而没有进行二进制转换。

它主要用于将一种数据类型从一种类型转换为另一种类型。它可以将一个指针转换成一个整数,也可以将一个整数转换成一个指针,在实际开发中,先把一个指针转换成一个整数,在把该整数转换成原类型的指针,还可以得到原来的指针值;特别是开辟了系统全局的内存空间,需要在多个应用程序之间使用时,需要彼此共享,传递这个内存空间的指针时,就可以将指针转换成整数值,得到以后,再将整数值转换成指针,进行对应的操作。

1
2
3
4
5
6
7
8
int a = 10;
double b = static_cast<double>(a);

printf("a = %d,b = %lf\n", a, b);
PRINT_INFO("" << __func__ << ":" << __LINE__ << ",a = " << a << ",b = " << b);

double c = reinterpret_cast<double&>(a);
PRINT_INFO("" << __func__ << ":" << __LINE__ << ",a = " << a << ",b = " << b << ",c = " << c);

参考文章:C++中的类型转换(static_cast、const_cast、dynamic_cast、reinterpret_cast)
参考文章:static_cast、dynamic_cast、const_cast和reinterpret_cast总结
参考文章:c++强制类型转换:dynamic_cast、const_cast 、static_cast、reinterpret_cast
参考文章:C++ 中static_cast、dynamic_cast、const_cast和reinterpret_cast总结