C++ 零碎知识点

yubo-guan / 2024-01-16 / 原文

目录
  • RTTI 运行时类型信息
  • explicit关键字


RTTI 运行时类型信息

简介
在C++中,RTTI(Run-Time Type Information,运行时类型信息)是一种机制,允许在程序执行期间确定对象的类型。RTTI是为了解决许多类库供应商自行实现此功能而导致的不兼容性问题而添加到C++语言中的。RTTI的主要目的是允许在运行时获取对象的实际类型信息。

在C++中,有三个主要的语言元素与运行时类型信息相关联:

  1. dynamic_cast运算符:用于多态类型的转换。
  2. typeid运算符:用于识别对象的确切类型。
  3. type_info类:用于保存typeid运算符返回的类型信息。

需要注意的是,RTTI主要适用于指针,但其中讨论的概念也适用于引用。RTTI仅适用于多态类,这意味着它们必须至少有一个虚拟函数。

C++中的RTTI允许在程序运行时获取对象的类型信息,这对于执行某些类型安全的操作和决策非常有用。

示例
当在C++中使用多态时,RTTI可以用于确定对象的实际类型。以下是一个简单的示例,演示了如何使用dynamic_casttypeid来实现多态类型的转换和类型识别。

#include <iostream>
#include <typeinfo>

class Base {
public:
    virtual ~Base() {}
};

class Derived : public Base {
public:
    void specificFunction() {
        std::cout << "This is a function specific to Derived class" << std::endl;
    }
};

int main() {
    Base* basePtr = new Derived;

    // 使用dynamic_cast进行类型转换
    Derived* derivedPtr = dynamic_cast<Derived*>(basePtr);
    if (derivedPtr) {
        derivedPtr->specificFunction();
    }

    // 使用typeid获取对象的类型信息
    if (typeid(*basePtr) == typeid(Derived)) {
        std::cout << "basePtr指向的对象是Derived类型" << std::endl;
    }

    delete basePtr;
    return 0;
}

在这个例子中,Base类是一个多态基类,Derived类是它的派生类。在main函数中,我们创建了一个Derived类的实例,并将其赋值给一个Base类的指针。然后,我们使用dynamic_castBase类的指针转换为Derived类的指针,并调用specificFunction。接着,我们使用typeid来检查basePtr指向的对象是否是Derived类型。

这个例子展示了如何在C++中使用RTTI来进行类型转换和类型识别,以实现多态行为。

使用场景
在实际项目中,我曾经遇到过使用RTTI的场景。一个具体的例子是在一个图形用户界面(GUI)库的开发中。在这个库中,我们需要处理各种不同类型的图形元素,例如按钮、文本框、复选框等。这些图形元素都是从一个基类派生而来的,因此在处理用户交互时,需要根据用户的操作来确定实际的图形元素类型。

在这种情况下,我们使用了RTTI来确定用户交互所涉及的图形元素的实际类型。通过使用dynamic_casttypeid,我们能够在运行时确定用户操作的对象类型,并执行相应的操作。例如,当用户点击一个按钮时,我们可以使用RTTI来确定该按钮的实际类型,并触发相应的事件处理。

总的来说,使用RTTI使得我们能够在运行时动态地确定对象的类型,从而实现了更加灵活和可扩展的图形用户界面库。这种经验让我意识到RTTI在处理多态类型时的价值,以及它在实际项目中的实用性。


explicit关键字

在C++中,explicit是一个关键字,用于指定构造函数或转换函数是否可以进行隐式转换和复制初始化。如果一个构造函数或转换函数被标记为explicit,则它不能用于隐式转换和复制初始化。这可以避免意外的类型转换,从而减少潜在的bug。

下面是一个使用explicit关键字的示例:

#include <iostream>
using namespace std;

class String {
public:
    explicit String(int n); // 分配n个字节给String对象
    String(const char *p); // 用char *p初始化对象
};

int main() {
    String mystring = 'x'; // 这里会导致编译错误,因为构造函数被标记为explicit
    String mystring2 = 10; // 这里也会导致编译错误,因为构造函数被标记为explicit
    String mystring3 = "hello"; // 这里是合法的,因为使用了直接初始化
}

在上面的示例中,如果构造函数没有被标记为explicit,那么String mystring = 'x';String mystring2 = 10;就会被隐式转换为String类型,从而导致潜在的bug。因此,使用explicit关键字可以避免这种情况的发生。