运行时类型特性相比于其他面向对象语言,C++更倾向于编译时处理 。如你之前所学 , 重写方法之所以有效,是因为方法与其实现之间存在一层间接关系,而不是因为对象内置了对其所属类的知识 。然而,C++中确实有一些特性提供了对对象的运行时视图 。这些特性通常被归为一组功能,称为运行时类型信息(RTTI) 。
RTTI提供了许多有用的特性 , 用于处理对象的类成员信息 。其中一个特性是 dynamic_cast(),它允许你在面向对象的层次结构中安全地在类型之间转换;这在本章前面已经讨论过 。在没有虚表(即没有虚方法)的类上使用 dynamic_cast() 会导致编译错误 。
文章插图
有趣且不寻常的继承问题RTTI的第二个特性是 typeid 运算符,它允许你在运行时查询对象的类型 。大多数情况下,你不应该需要使用 typeid,因为基于对象类型有条件地运行的代码最好通过虚方法处理 。以下代码使用 typeid 根据对象的类型打印消息:
import <typeinfo>;class Animal { public: virtual ~Animal() = default; };class Dog : public Animal {};class Bird : public Animal {};void speak(const Animal& animal) {if (typeid(animal) == typeid(Dog)) {cout << "Woof!" << endl;} else if (typeid(animal) == typeid(Bird)) {cout << "Chirp!" << endl;}}
每当你看到这样的代码时,你应该立即考虑使用虚方法重新实现功能 。在这种情况下 , 更好的实现方式是在 Animal 类中声明一个名为 speak() 的虚方法 。Dog 类重写该方法以打印 "Woof!",而 Bird 类重写该方法以打印 "Chirp!" 。这种方法更符合面向对象编程的思想,即将与对象相关的功能赋予这些对象 。警告:typeid 运算符只有在类至少有一个虚方法时才能正确工作 , 即当类有虚表时 。此外,typeid 运算符会从其参数中去除引用和常量修饰符 。typeid 运算符可能对于日志记录和调试目的有用 。以下代码展示了如何使用 typeid 进行日志记录 。logObject() 函数接受一个可记录的对象作为参数 。这种设计使得任何可以被记录的对象都继承自 Loggable 类,并支持一个名为 getLogMessage() 的方法 。
class Loggable { public: virtual ~Loggable() = default; virtual std::string getLogMessage() const = 0; };class Foo : public Loggable { public: std::string getLogMessage() const override { return "Hello logger."; } };
继承技巧的发现class Loggable {public:virtual ~Loggable() = default;virtual std::string getLogMessage() const = 0;};class Foo : public Loggable {public:std::string getLogMessage() const override {return "Hello logger.";}};void logObject(const Loggable& loggableObject) {cout << typeid(loggableObject).name() << ": ";cout << loggableObject.getLogMessage() << endl;}
logObject() 函数首先将对象类的名称写入输出流,然后是其日志消息 。这样,当你稍后阅读日志时,你可以看到每条写入的行是由哪个对象负责的 。以下是使用 Microsoft Visual C++ 2019 编译并调用 logObject() 函数时生成的输出示例:class Foo: Hello logger.
如你所见,由 typeid 运算符返回的名称是 “class Foo” 。然而,这个名称依赖于你使用的编译器 。例如,如果你使用 GCC 编译相同的代码,输出将如下所示:【C++ 运行时类型信息与继承技巧探索】
3Foo: Hello logger.
注意:如果你使用 typeid 进行的目的不是日志记录和调试 , 请考虑使用虚方法重新实现它 。
推荐阅读
- 解开C++之call_once的神秘面纱:记一个有意思的问题笔记
- C++高级编程:构建高效稳定接口与深入对象设计技巧
- C++函数返回指针和引用的坑
- 深入理解C++方法重载、内联与高级用法
- 元宵节花灯类型多样 元宵节花灯的种类,形状,色彩
- 睡眠障碍有哪些类型 睡眠障碍有哪些类型的症状
- 称的种类
- 查看电脑系统位数:轻松掌握操作系统类型
- 探索 C++20 的新领域:深入理解 static 关键字和核心语言特性测试宏
- 快递托寄物是什么意思 快递托寄物类型是什么