C++ 20类型转换指南:使用场景与最佳实践

307次阅读  |  发布于6月以前

类型转换 (Casts)

C++ 提供了五种特定的类型转换:const_cast<>()static_cast<>()reinterpret_cast<>()dynamic_cast<>() 和 C++20 引入的 std::bit_cast<>()

请注意,旧的 C 风格类型转换(如 (int)myFloat)在 C++ 中仍然有效,并在现有代码库中广泛使用。C 风格的类型转换涵盖了所有四种 C++ 类型转换,因此它们更容易出错,因为您试图实现的目的并不总是显而易见的,可能会得到意外的结果。我强烈建议您在新代码中只使用 C++ 风格的类型转换,因为它们更安全,且在代码中更加突出。

虚拟基类

模糊基类出现在多个父类共有一个共同的父类时。推荐的解决方案是确保共享的父类自身不具有任何功能。这样,其方法永远不会被调用,从而避免了歧义问题。C++ 还有另一种机制,称为虚拟基类,用于解决您希望共享的父类具有自己功能的情况。

如果共享的父类是一个虚拟基类,则不会有任何歧义。以下代码在 Animal 基类中添加了一个 sleep() 方法,并修改了 DogBird 类,使它们作为虚拟基类从 Animal 继承。如果不使用虚拟基类,对 DogBird 对象的 sleep() 调用将是模糊的,并会生成编译器错误,因为 DogBird 将具有两个 Animal 子对象,一个来自 Dog,一个来自 Bird。然而,当 Animal 被虚拟继承时,DogBird 只有一个 Animal 类的子对象,因此调用 sleep() 不会有歧义。

class Animal {
public:
    virtual void eat() = 0;
    virtual void sleep() { cout << "zzzzz...." << endl; }
};

class Dog : public virtual Animal {
public:
    virtual void bark() { cout << "Woof!" << endl; }
    void eat() override { cout << "The dog ate." << endl; }
};

class Bird : public virtual Animal {
public:
    virtual void chirp() { cout << "Chirp!" << endl; }
    void eat() override { cout << "The bird ate." << endl; }
};

class DogBird : public Dog, public Bird {
public:
    void eat() override { Dog::eat(); }
};

int main() {
    DogBird myConfusedAnimal;
    myConfusedAnimal.sleep(); // 因为虚拟基类而不模糊
}

注意:虚拟基类是避免类层次结构中歧义的好方法。

类型转换 (Casts)

C++ 提供了五种特定的类型转换:const_cast<>()static_cast<>()reinterpret_cast<>()dynamic_cast<>() 和 C++20 引入的 std::bit_cast<>()。第一种在第 1 章中讨论过。第 1 章还介绍了用于某些基本类型之间转换的 static_cast<>(),但在继承的上下文中还有更多内容。现在您已经熟悉编写自己的类并理解类继承,是时候更仔细地看看这些类型转换了。

请注意,旧的 C 风格类型转换(如 (int)myFloat)在 C++ 中仍然有效,并在现有代码库中广泛使用。C 风格的类型转换涵盖了所有四种 C++ 类型转换,因此它们更容易出错,因为您试图实现的目的并不总是显而易见的,可能会得到意外的结果。我强烈建议您在新代码中只使用 C++ 风格的类型转换,因为它们更安全,且在代码中更加突出。

static_cast()

使用场景

int i { 3 };
int j { 4 };
double result { static_cast<double>(i) / j };

在继承中的应用

class Base { /* ... */ };
class Derived : public Base { /* ... */ };

Base* b { nullptr };
Derived* d { new Derived{} };
b = d; // 向上转型,不需要转换。
d = static_cast<Derived*>(b); // 向下转型,需要转换。

注意事项

reinterpret_cast()

使用场景

注意事项

std::bit_cast()

特点

示例


float asFloat { 1.23f };
auto asUint { bit_cast<unsigned int>(asFloat) };
if (bit_cast<float>(asUint) == asFloat) {
    cout << "Roundtrip success." << endl;
}

应用场景

  1. 无自定义析构函数:类型没有自定义的析构函数。
  2. 无自定义或虚拟构造函数:类型没有自定义的构造函数,也没有虚拟构造函数。
  3. 无虚函数和虚基类:类型不包含虚函数,并且不从虚基类继承。
  4. 可简单拷贝其状态:类型的所有成员可以通过简单的内存拷贝来复制,没有需要特殊处理的成员(如指针或动态分配的资源)。

dynamic_cast()

特点

示例

Base* b;
Derived* d { new Derived{} };
b = d;
d = dynamic_cast<Derived*>(b);

Base base;
Derived derived;
Base& br { base };
try {
    Derived& dr { dynamic_cast<Derived&>(br) };
} catch (const bad_cast&) {
    cout << "Bad cast!" << endl;
}

与其他类型转换的区别

C++ 类型转换总结

情境推荐的转换方法说明移除 const 属性const_cast()用于移除对象的 const 属性语言直接支持的显式转换static_cast()例如,从 int 转换到 doublebool用户定义的构造函数或转换支持的显式转换static_cast()用于用户定义的转换一个类的对象转换为另一个(无关)类的对象bit_cast()用于无关类之间的对象转换同一继承层次中的类的指针对象转换dynamic_cast() (推荐) 或 static_cast()用于继承层次中的指针对象转换同一继承层次中的类的引用对象转换dynamic_cast() (推荐) 或 static_cast()用于继承层次中的引用对象转换不相关类型的指针转换reinterpret_cast()用于完全不相关的指针类型之间的转换不相关类型的引用转换reinterpret_cast()用于完全不相关的引用类型之间的转换函数指针之间的转换reinterpret_cast()用于函数指针之间的转换

注意事项

Copyright© 2013-2020

All Rights Reserved 京ICP备2023019179号-8