zoukankan      html  css  js  c++  java
  • 运行时类型识别

    1.RTTI

    1)运行时类型识别RTTI(Run-Time Type Identification),它能够获取基类指针或引用所指向的对象的实际类型,在C++中,为了支持RTTI提供了两个运算符:typeid和dynamic_cast

    2)当某种情况下无法使用虚函数时,却要完成类似于虚函数的功能时,可以使用RTTI,后面将会举例讲解这种情况

    2.dynamic_cast

    2.1概念

    1)dynamic_cast运算符用于将基类的指针或引用安全地转换成派生类的指针或引用,这是安全的“向下转型”,至于“向上转型”,即派生类指针或引用转换为其基类指针或引用,本身就是安全的,尽管可以使用dynamic_cast进行转换,但这是没必要的, 普通的转换已经可以达到目的,毕竟使用dynamic_cast是有开销的

    2)dynamic_cast使用形式:dynamic_cast<type*>(expression)

      dynamic_cast<type&>(expression)

      dynamic_cast<type&&>(expression)   //右值引用

    其中type必须是一个类类型,并且通常具有虚函数,否则编译会报错

    3)当 expression 是基类指针或引用时:

    • 如果此基类指针或引用所指向对象是派生类类型的,这种转换是安全的,可以成功
    • 如果基类指针或引用所指向对象为基类类型,这种转化是不安全的,会失败

    4)对于指针,转换失败了,则返回结果0,即返回的指针为NULL,不会抛出bad_cast异常

    5)对于引用,转换失败了,则会抛出bad_cast异常

    class Base {
    public:
        Base() {};
        virtual void Show() { cout << "This is Base calss"; }
    };
    
    class Derived :public Base {
    public:
        Derived() {};
        void Show() { cout << "This is Derived class"; }
    };
    
    int main()
    {
        //第一种情况,转换成功
        Derived d;
        Base& base1 =d;
        Derived& der1 = dynamic_cast<Derived&>(base1);
        cout << "第一种情况:";
        der1.Show();
        cout << endl;
    
        //第二种情况
        Base b;
        Base &base2 = b;
        cout << "第二种情况:";
        try {
            Derived& der2 = dynamic_cast<Derived&>(base2);
        }
        catch (bad_cast)
        {
            cout << "转化失败,抛出bad_cast异常" << endl;
        }
    
        return 0;
    }

    3.typeid

    3.1概念

    1)typeid运算符用于返回表达式的类型

    2)typeid使用形式:typeid(expression);

      如果表达式的类型是类且至少包含有一个虚函数,则typeid返回表达式的动态类型,需要在运行时计算;否则,typeid操作符返回表达式的静态类型,在编译时就可以计算

    3)typeid运算符的返回值是type_info类的引用,ype_info类在头文件typeinfo中定义

    4)type_info类提供了public虚析构函数,以使用户能够用其作为基类。它的默认构造函数和拷贝构造函数及赋值操作符都定义为private,所以不能定义或复制type_info类的对象,程序中创建type_info对象的唯一方法是使用typeid运算符

    3.2实例

    class Base {};
    class Derived: public Base {};
    
    int main()
    {
        Base b, *pb;
        pb = NULL;
        Derived d;
    
        cout << typeid(int).name() << endl
             << typeid(unsigned).name() << endl
             << typeid(long).name() << endl
             << typeid(unsigned long).name() << endl
             << typeid(char).name() << endl
             << typeid(unsigned char).name() << endl
             << typeid(float).name() << endl
             << typeid(double).name() << endl
             << typeid(string).name() << endl
             << typeid(Base).name() << endl
             << typeid(b).name()<<endl
             << typeid(pb).name()<<endl
             << typeid(Derived).name() << endl
             << typeid(d).name()<<endl
             << typeid(type_info).name() << endl;
             
        return 0;
    }

    VS和GCC的结果:

    class Base {};
    class Derived : public Base {};
    
    int main()
    {
        Base b, *pb;
        pb = NULL;
        Derived d;
        Base* pb2 = new Derived;//向上转型是安全的
        Base& b2 = d;
        Base* pb3 = &d;
        cout << typeid(pb2).name() << endl//输出Base *
            << typeid(b2).name() << endl //输出Base,b2是一个类的引用,但这个类没有虚函数
            << typeid(pb3).name() << endl//输出Base *,pb3是一个指针
            << typeid(*pb3).name() << endl;//输出Base,*pb3是一个类,但这个类没有虚函数
    
        return 0;
    }

    现在对Base加上一个虚函数:

    class Base {
    public:
        virtual void f() {}
    };
    class Derived : public Base {};
    
    int main()
    {
        Base b, *pb;
        pb = NULL;
        Derived d;
        Base* pb2 = new Derived;//向上转型是安全的
        Base& b2 = d;
        Base* pb3 = &d;
        cout << typeid(pb2).name() << endl//输出Base *,pb2是一个指针,不是类对象
            << typeid(b2).name() << endl //输出Derived,b2是一个基类的引用,这个类有虚函数,返回动态类型
            << typeid(pb3).name() << endl//输出Base *,pb3是一个指针,不是类对象
            << typeid(*pb3).name() << endl;//输出Derived,*pb3是一个类对象,这个类有虚函数,返回动态类型
    
    
        return 0;
    }

    4.使用RTTI

    4.1实例

      为具有继承关系的类实现相等运算符

    4.2分析

    1)对于两个对象来书,如果它们的类型相同并且对应的数据成员取值相同,则视为相等

    2)对具有继承体系的类实现“==”,必须考虑比较对象在体系中所处的位置,因为派生类相对于基类有着自己的新成员

    3)一种容易想到的解决方案是定义一套虚函数,令其在各个层次上分别执行相等性判断;遗憾的是,这个方案其实不能凑效,因为虚函数的基类版本和派生类版本的形参必须完全相同,如果定义一个虚函数equal,则它的形参必须是基类的引用,此时,equal函数就只能比较基类的成员,而不能比较派生类的成员                                                     

    4)使用RTTI解决问题:

    • 相等运算符的形参是基类的引用,使用typeid检查两个对象的类型是否一致
    • 若不一致,直接返回false;若一致,则调用虚函数equal继续判断,在虚函数的派生类版本中调用dynamic_cast将基类的引用转换为派生类的引用,这样才能比较派生类的成员
    class Base {
        friend bool operator==(const Base&, const Base&);//相等运算符为Base的友元
    public:
        // Base其他成员
    protected:
        virtual bool equal(const Base&) const;//虚函数equal
    };
    
    class Derived : public Base {
    public:
        // Derived的其他成员
    protected:
        bool equal(const Base&) const;
    
    };
    
    bool operator==(const Base &lhs, const Base &rhs)//作为Base的友元,可以使用Base的非public成员
    {
        //当运算对象类型相同时才调用虚函数equal
        return typeid(lhs) == typeid(rhs) && lhs.equal(rhs);//运算对象均为Base对象时,调用Base::equal;运算对象均为Derived对象时,调用Derived::equal
        
    }
    
    bool Derived::equal(const Base &rhs) const
    {
        // 基类引用绑定在派生类对象上,这个转换是成功的;同时这个转换必不可少,因为*this是Derived对象,形参是Base对象,执行了转换后,才能访问形参对象的派生类成员
        const Derived& r = dynamic_cast<const Derived&>(rhs);
    
        // 比较两个Derived对象是否相等
        ……
    }
    
    bool Base::equal(const Base &rhs) const
    {
        // 无需先转换形参,因为*this和形参都是Base对象
        // 比较Base对象
    }
  • 相关阅读:
    什么是架构
    jenkins权限插件配置
    解决Error response from daemon: oci runtime error: container_linux.go:247: starting container process
    解决“/tmp/crontab bad minute”问题
    yml链接mysql路径serverTimezone=UTC的那些坑
    java请求头导致特殊字符为空问题
    使用ssh连接WSL
    系统设计与任务分配
    需求规格说明书
    选题报告
  • 原文地址:https://www.cnblogs.com/Joezzz/p/10436459.html
Copyright © 2011-2022 走看看