C++ 运行时类型信息与继承技巧探索

开发 前端
C++中确实有一些特性提供了对对象的运行时视图。这些特性通常被归为一组功能,称为运行时类型信息(RTTI)。

运行时类型特性

相比于其他面向对象语言,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 编译相同的代码,输出将如下所示:

3Foo: Hello logger.

注意:如果你使用 typeid 进行的目的不是日志记录和调试,请考虑使用虚方法重新实现它。

责任编辑:赵宁宁 来源: coding日记
相关推荐

2010-01-27 14:14:48

C++程序运行时间

2011-08-19 15:05:29

异常处理

2022-12-30 08:08:30

2015-07-20 15:44:46

Swift框架MJExtension反射

2024-03-21 09:15:58

JS运行的JavaScrip

2011-12-27 09:39:12

C#运行时

2009-09-24 17:19:06

运行时多态性

2024-01-08 08:24:02

PythonSys 模块工具

2020-09-28 15:54:18

Python语言技术

2018-06-24 15:23:05

软件工程环境开发

2021-09-11 15:38:23

容器运行镜像开放

2019-07-12 09:30:12

DashboardDockerDNS

2009-02-10 09:03:59

动态语言CLRVB.NET

2024-03-20 10:46:00

云原生容器

2020-12-07 13:31:43

GoMutex开发者

2013-11-26 16:49:55

Android开发运行时KitKat

2021-08-18 08:32:09

代码运行时间示波器

2023-01-03 09:10:21

2023-07-28 10:42:43

2022-01-19 08:50:53

设备树Linux文件系统
点赞
收藏

51CTO技术栈公众号