C++——简单的RTTI

即运行阶段类型识别(Runtime Type Identification)

 C++有三个支持RTTI的元素

1.dynamic_cast,使用一个基类的指针生成一个指向派生类的指针,失败返回0,即空指针。

尖括号表示括号中的类型能否安全的转为尖括号中的类型

本质上就是个更加规范的类型转换

2.typeid, 返回一个指出对象类型的值(可接受类名和结果为对象的表达式)

3.type_info结构体,存储了有关特定类型的信息。

dynamic_cast

#include <iostream>
#include <cstdlib>
#include <ctime>

using namespace std;

class Grand
{
private:
    int hold;
public:
    explicit Grand(int h = 0) : hold(h) {}
    virtual void Speak() const
    {
        cout << "I am a Grand class\n";
    }
    virtual int Value() const
    {
        return hold;
    }
};

class Superb : public Grand
{
public:
    explicit Superb(int h) : Grand(h) {}
    void Speak() const override
    {
        cout << "I am a Superb class\n";
    }
    virtual void Say() const
    {
        cout << "I hold the superb value of " << Value() << endl;
    }
};

class Magnificent : public Superb
{
private:
    char ch;
public:
    explicit Magnificent(int h=0, char c = 'A') : Superb(h), ch(c) {}
    void Speak() const override
    {
        cout << "I am a Magnificent class\n";
    }
    void Say() const override
    {
        cout << "I hold the char" << ch << "and the int " << Value() << endl;
    }
};

Grand * GetOne();
int main()
{
    srand(time(NULL));
    //基类指针
    Grand * pg;
    //派生类指针
    Superb * ps;
    for(int i=0;i<5;i++){
        //基类指针随机指向一个对象
        pg = GetOne();
        pg->Speak();
        //RTTI判断能否安全转换
        //使用一个基类指针(pg)生成一个派生类指针(ps)
        if(ps = dynamic_cast<Superb*>(pg)){
            ps->Say();
        }else
            cout << "不安全的转换" << endl;
    }
    return 0;
}

Grand * GetOne()
{
    Grand * p;
    switch (rand() % 3) {
        case 0 : p = new Grand(rand()%100);
            break;
        case 1 : p = new Superb(rand()%100);
            break;
        case 2 : p = new Magnificent(rand()%100, 'A'+rand()%26);
            break;
    }
    return p;
}

运行结果 

C++——简单的RTTI

 因为派生类指针不能指向基类,所以在这里C++——简单的RTTI

 不会调用Say()函数。这里的功劳就要归功于dynamic_cast<Superb*>(pg)他了

关于这里的语法可能有一些奇怪,赋值表达式的值是他的左值,如果转换成功,则ps的值为非零(true)。如果类型转换失败,则返回一个空指针(0,即false)。

特别的,当用引用作为参数时,应该用try,catch语句捕获错误。

也可用typeid函数,不用dynamic_cast,改为分别的以下判断并且使用强制转换。

//注意括号里的是对象,不是对象指针
typeid(Superb) == typeid(*pg);

但其实这样写不仅代码变长,也存在一些缺陷,比如需要靠大量的else if维护(或者switch)。

 typeid 运算符 和 type_info 结构体

前者能够确定两个对象是否为同种类型,并返回一个后者对象的引用(<typeinfo>中定义)。

type_info类重载了 == 和 != 。

如果有空指针比较(*pg(nullptr)),引发bad_typeid异常(异常类型从exception派生而来)。

其中type_info的定义

class type_info
{
private:
    type_info(const type_info&);
    type_info& operator=(const type_info&);//type_info类的复制构造函数和赋值运算符是私有的。
public:
    virtual ~type_info();//析构函数
    bool operator==(const type_info&)const;//在type_info类中重载了==运算符,该运算符可以比较两个对象的类型是否相等。
    bool operator!=(const type_info&)const;//重载的!=运算符,以比较两个对象的类型是否不相等
    const char* name()const;//使用得较多的成员函数name,该函数返回对象的类型的名字。前面使用的typeid(a).name()就调用了该成员函数
    bool before(const type_info&);
};

对上面的程序进行小小的修改

        if(ps = dynamic_cast<Superb*>(pg)){
            ps->Say();
        }else
            cout << "不安全的转换" << endl;
        if(typeid(*pg)==typeid(Magnificent)){
            cout << "生成了一个Magnificent类,好耶!";
            //type_inf定义的类方法,返回const char *(即类名)
            cout << "use .name : " << typeid(*pg).name() << endl;
        }

输出结果

C++——简单的RTTI 

 

上一篇:[ICLR 2021] Revisiting Dynamic Convolution via Matrix Decomposition 学习笔记


下一篇:C#基础——超级方便的ExpandoObject类别