# C++ 几种类型转换

面试高频指数:★★★☆☆

在 C 语言中,我们大多数是用 (type_name) expression 这种方式来做强制类型转换,但是在 C++ 中,更推荐使用四个转换操作符来实现显式类型转换:

  • static_cast
  • dynamic_cast
  • const_cast
  • reinterpret_cast

# 一、static_cast

用法: static_cast <new_type> (expression)

其实 static_cast 和 C 语言 () 做强制类型转换基本是等价的。

主要用于以下场景:

# 1.1 基本类型之间的转换

将一个基本类型转换为另一个基本类型,例如将整数转换为浮点数或将字符转换为整数。

int a = 42;
double b = static_cast<double>(a); // 将整数a转换为双精度浮点数b

# 1.2 指针类型之间的转换

将一个指针类型转换为另一个指针类型,尤其是在类层次结构中从基类指针转换为派生类指针。这种转换不执行运行时类型检查,可能不安全,要自己保证指针确实可以互相转换。

class Base {};
class Derived : public Base {};

Base* base_ptr = new Derived();
Derived* derived_ptr = static_cast<Derived*>(base_ptr); // 将基类指针base_ptr转换为派生类指针derived_ptr

# 1.3 引用类型之间的转换

类似于指针类型之间的转换,可以将一个引用类型转换为另一个引用类型。在这种情况下,也应注意安全性。

Derived derived_obj;
Base& base_ref = derived_obj;
Derived& derived_ref = static_cast<Derived&>(base_ref); // 将基类引用base_ref转换为派生类引用derived_ref

static_cast在编译时执行类型转换,在进行指针或引用类型转换时,需要自己保证合法性。

如果想要运行时类型检查,可以使用dynamic_cast进行安全的向下类型转换。

# 二、dynamic_cast

用法: dynamic_cast <new_type> (expression)

dynamic_cast在C++中主要应用于父子类层次结构中的安全类型转换。

它在运行时执行类型检查,因此相比于static_cast,它更加安全。

dynamic_cast的主要应用场景:

# 2.1 向下类型转换

当需要将基类指针或引用转换为派生类指针或引用时,dynamic_cast可以确保类型兼容性。

如果转换失败,dynamic_cast将返回空指针(对于指针类型)或抛出异常(对于引用类型)。

class Base { virtual void dummy() {} };
class Derived : public Base { int a; };

Base* base_ptr = new Derived();
Derived* derived_ptr = dynamic_cast<Derived*>(base_ptr); // 将基类指针base_ptr转换为派生类指针derived_ptr,如果类型兼容,则成功

# 2.2 用于多态类型检查

处理多态对象时,dynamic_cast可以用来确定对象的实际类型,例如:

class Animal { public: virtual ~Animal() {} };
class Dog : public Animal { public: void bark() { /* ... */ } };
class Cat : public Animal { public: void meow() { /* ... */ } };

Animal* animal_ptr = /* ... */;

// 尝试将Animal指针转换为Dog指针
Dog* dog_ptr = dynamic_cast<Dog*>(animal_ptr);
if (dog_ptr) {
    dog_ptr->bark();
}

// 尝试将Animal指针转换为Cat指针
Cat* cat_ptr = dynamic_cast<Cat*>(animal_ptr);
if (cat_ptr) {
    cat_ptr->meow();
}

另外,要使用dynamic_cast有效,基类至少需要一个虚拟函数。

因为,dynamic_cast只有在基类存在虚函数(虚函数表)的情况下才有可能将基类指针转化为子类。

# 2.3 dynamic_cast 底层原理

dynamic_cast的底层原理依赖于运行时类型信息(RTTI, Runtime Type Information)。

C++编译器在编译时为支持多态的类生成RTTI,它包含了类的类型信息和类层次结构。

我们都知道当使用虚函数时,编译器会为每个类生成一个虚函数表(vtable),并在其中存储指向虚函数的指针。

伴随虚函数表的还有 RTTI(运行时类型信息),这些辅助的信息可以用来帮助我们运行时识别对象的类型信息。

《深度探索C++对象模型》中有个例子:

class Point
{
public:
	Point(float xval);
	virtual ~Point();
 
	float x() const;
	static int PointCount();
 
protected:
	virtual ostream& print(ostream& os) const;
 
	float _x;
	static int _point_count;
};

首先,每个多态对象都有一个指向其vtable的指针,称为vptr。

RTTI(就是上面图中的 type_info 结构)通常与vtable关联。

dynamic_cast就是利用RTTI来执行运行时类型检查和安全类型转换。

以下是dynamic_cast的工作原理的简化描述:

  1. 首先,dynamic_cast通过查询对象的 vptr 来获取其RTTI(这也是为什么 dynamic_cast 要求对象有虚函数)
  2. 然后,dynamic_cast比较请求的目标类型与从RTTI获得的实际类型。如果目标类型是实际类型或其基类,则转换成功。
  3. 如果目标类型是派生类,dynamic_cast会检查类层次结构,以确定转换是否合法。如果在类层次结构中找到了目标类型,则转换成功;否则,转换失败。
  4. 当转换成功时,dynamic_cast返回转换后的指针或引用。
  5. 如果转换失败,对于指针类型,dynamic_cast返回空指针;对于引用类型,它会抛出一个std::bad_cast异常。

因为dynamic_cast依赖于运行时类型信息,它的性能可能低于其他类型转换操作(如static_cast),static 是编译器静态转换,编译时期就完成了。

# 三、const_cast

用法: const_cast <new_type> (expression) new_type 必须是一个指针、引用或者指向对象类型成员的指针。

# 3.1 修改const对象

当需要修改const对象时,可以使用const_cast来删除const属性。

const int a = 42;
int* mutable_ptr = const_cast<int*>(&a); // 删除const属性,使得可以修改a的值
*mutable_ptr = 43; // 修改a的值

# 3.2 const对象调用非const成员函数

当需要使用const对象调用非const成员函数时,可以使用const_cast删除对象的const属性。

class MyClass {
public:
    void non_const_function() { /* ... */ }
};

const MyClass my_const_obj;
MyClass* mutable_obj_ptr = const_cast<MyClass*>(&my_const_obj); // 删除const属性,使得可以调用非const成员函数
mutable_obj_ptr->non_const_function(); // 调用非const成员函数

不过上述行为都不是很安全,可能导致未定义的行为,因此应谨慎使用。

# 四、reinterpret_cast

用法: reinterpret_cast <new_type> (expression)

reinterpret_cast用于在不同类型之间进行低级别的转换。

首先从英文字面的意思理解,interpret是“解释,诠释”的意思,加上前缀“re”,就是“重新诠释”的意思;

cast 在这里可以翻译成“转型”(在侯捷大大翻译的《深度探索C++对象模型》、《Effective C++(第三版)》中,cast都被翻译成了转型),这样整个词顺下来就是“重新诠释的转型”。

它仅仅是重新解释底层比特(也就是对指针所指针的那片比特位换个类型做解释),而不进行任何类型检查。

因此,reinterpret_cast可能导致未定义的行为,应谨慎使用。

reinterpret_cast的一些典型应用场景:

# 4.1 指针类型之间的转换

在某些情况下,需要在不同指针类型之间进行转换,如将一个int指针转换为char指针。

这在 C 语言中用的非常多,C语言中就是直接使用 () 进行强制类型转换

int a = 42;
int* int_ptr = &a;
char* char_ptr = reinterpret_cast<char*>(int_ptr); // 将int指针转换为char指针

另外,这篇文章对于 reinterpret_cast 讲解得很不错,可以看一下:

https://zhuanlan.zhihu.com/p/33040213

最新原创的文章都先发布在公众号,欢迎关注哦~,
扫描下方二维码回复「CS」可以获得我汇总整理的计算机学习资料~

编程指北图片
@2021-2024 编程指北 版权所有 粤ICP备2021169086号-2