C++ 强制转换运算符

一、static_cast

static_cast 在编译时进行类型转换,在转换时会进行一些类型检查以确保转换是相对安全的,但不包括运行时类型检查static_cast 无法强制转换掉 constvolatile__unaligned 特性。

语法:仅根据表达式中存在的类型,将 expression 转换为 type 类型。

static_cast <type> ( expression )

应用场景

  • 可用于执行任何隐式转换,包括标准转换和用户定义的转换。
  • 整数值与枚举类型之间转换, 如果整型值不在枚举值的范围内,生成的枚举值是不确定的。
  • 将任何指针类型显式转换为 void*类型 ,将void*转换为目标类型指针,0转换为任何指针类型。
  • 类继承情况下的向上类型转换和向下类型转换
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
/* 隐式转换:例如数值类型转换,但程序员需要确保目标类型不会越界 */  
char ch;
int i = 65;
float f = 2.5;
int j;
int x = 650;
ch = static_cast<char>(i); // int to char, ch -> 'A'
j = static_cast<double>(f); // float to int, j -> 2
ch = static_cast<char>(x); // char范围为 -128-127,x为650,越界,ch存的是错误的值


class integer {
int _x;
public:
integer(int x = 0)
: _x(x)
{
cout << "integer()" << endl;
}

// 用户定义的类型转换
operator string()
{
cout << "operator string()" << endl;
return to_string(_x);
}
};

integer obj(3); // integer()
string str = obj; // operator string()
obj = 20; // integer()
string str2 = static_cast<string>(obj); // operator string()
obj = static_cast<integer>(30); // integer()

/* 整数与枚举类型转换 */
enum class Color {
RED,
GREEN,
BLUE
};

Color color = Color::GREEN;
int colorValue = static_cast<int>(color); // 将枚举类型转换为整数,colorValue=1
int intValue = 2;
Color enumColor = static_cast<Color>(intValue); // 将整数转换为枚举类型, enumColor=Color::BLUE

/* 将任何指针类型显式转换为 `void*`类型 ,将`void*`转换为目标类型指针 */
int i = 65;
void *pv = static_cast<void *>(&i);
char *pc = static_cast<char *>(pv); // *pc为 'A'
int* pi = static_cast(int *)(nullptr);

/*
向上与向下类型转换,向下类型转换是不安全的,它在编译时进行类型检查,而不会进行运行时的安全性检查。
如果基类指针并非完整指向派生类对象,向下类型转换可能带来灾难。
*/
class Base {
public:
void display() {
std::cout << "Inside Base class" << std::endl;
}
};

class Derived : public Base {
public:
void show() {
std::cout << "Inside Derived class" << std::endl;
}
};

Derived derivedObj;
Base* basePtr = static_cast<Base*>(&derivedObj); // Upcasting
basePtr->display(); // Accessing Base class member
// basePtr->show(); // Error: Base class pointer cannot access Derived class member

Base* basePtr = new Derived;
Derived* DerivedPtr = static_cast<Derived*>(basePtr); // Downcasting

二、dynamic_cast

dynamic_cast 用于具有虚函数的基类与派生类之间指针与引用的转换。编译器会运行时进行类型检查,如果转换是合法的,则转换成功并返回指向目标类型的指针或引用;如果转换不合法,则返回空指针(对指针类型)或抛出 std::bad_cast 异常(对引用类型)。

语法:将 expression 转换为 type 类型。

dynamic_cast <type> ( expression )

应用场景

  • 向上类型转换(更推荐使用static_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
/* 向下类型转换 */
class Base {
public:
virtual void show() {
cout << "Base class" << endl;;
}
};

class Derived : public Base {
public:
virtual void show() {
cout << "Derived class" << endl;
}
};

Base *basePtr = new Derived();
Derived *derivedPtr = dynamic_cast<Derived *>(basePtr);
if (derivedPtr) {
derivedPtr->show(); // 输出:Derived class
} else {
cout << "Failed to cast" << endl;
}

Derived derivedObj;
Base &baseRef = derivedObj;
try {
Derived &derivedRef = dynamic_cast<Derived &>(baseRef);
derivedRef.show(); // 输出:Derived class
}
catch (std::bad_cast) {
cout << "Can't cast to Derived&" << endl;
}
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
/* 
多继承向上类型转换,指向 D 类型对象的指针可以安全地强制转换为 B 或 C。 但是,如果 D 强制转换为指向 A 对象的指针,会产生 A 的哪个实例? 这将导致不明确的强制转换错误。 若要解决此问题,可以执行两个明确的强制转换
*/


class A {
virtual void f();
};

class B : public A {
virtual void f();
};

class C : public A {
virtual void f();
};

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


D *pd = new D;
A *pa = dynamic_cast<A *>(pd); // C4540, ambiguous cast fails at runtime
B *pb = dynamic_cast<B *>(pd); // first cast to B
A *pa2 = dynamic_cast<A *>(pb); // ok: unambiguous
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
/*
在给定 E 类型对象和指向 D 子对象的指针的情况下,若要从 D 子对象导航到最左侧的 A 子对象,可以进行三次转换。 可以执行从 D 指针到 E 指针的 dynamic_cast 转换,然后执行从 E 到 B 的转换(可以是 dynamic_cast,也可以是隐式转换),最后执行从 B 到 A 的隐式转换
*/

class A {
virtual void f();
};

class B : public A {
virtual void f();
};

class C : public A {
};

class D {
virtual void f();
};

class E : public B, public C, public D {
virtual void f();
};


D *dPtr = new E();
E *pe = dynamic_cast<E *>(dPtr);
B *pb = pe; // upcast, implicit conversion
A *pa = pb; // upcast, implicit conversion

/*
横向类型转换
*/
D *dPtr = new E();
B *pb = dynamic_cast<B *>(dPtr); // 横向类型转换
A* pa = pb; // upcast, implicit conversion

三、const_cast

移除对象的 constvolatile__unaligned 属性,如果一个对象本身就是const属性,使用const_cast,结果是C++标准未定义的。

语法:

const_cast <type> ( expression )

应用场景

  • 函数形参是non-const变量,并且函数不会对实参的值进行改动,但变量为const
  • 变量为non-const,使用一个const的指针(引用)来指向它,某个地方需要修改指针(引用)指向的值
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
void print(int *num)
{
cout << *num <endl;
}

const int i = 1;
print(const_cast<int*>(&i));


int j = 10;
const int *pi_c = &j;
int *pi = const_cast<int *>(pi_c);
*pi = 1; // j为1

const int &ri_c = j;
int &ri = const_cast<int &>(ri_c);
ri = 2; // j为2

/* 警告:i本身为const,使用const_cast行为是未定义的 */
const int i = 10;
const int *pi_c = &i;
int *pi = const_cast<int *>(pi_c);
*pi = 1;
cout << "i: " << i << " adderss: " << &i << endl; // i: 10 adderss: 0x30c624598
cout << "*pi: " << *pi << " adderss: " << pi << endl; // *pi: 1 adderss: 0x30c624598

四、reinterpret_cast

允许将任何指针(引用)转换为任何其他指针(引用)类型。 也允许将任何整数类型转换为任何指针(引用)类型以及反向转换。reinterpret_cast 运算符无法强制转换掉 constvolatile__unaligned 特性。滥用 reinterpret_cast 运算符可能很容易带来风险。

语法

reinterpret_cast < type > ( expression )

应用场景

  • 改变指针或引用的类型
  • 将指针或引用转换为一个足够长度的整型
  • 将整型转换为指针或引用类型