• [C++对象模型][10]类型转化


    一 typeid与dynamic_cast

    1)RTTI, Runtime Type Identification (RTTI) or Run-time type information (RTTI),表示在运行时动态决定变量的类型,来调用正确的虚函数。 RTTI在VS2008中默认为关闭,可以通过修改编译选项Enable Run-Time Type Info 为 Yes,来启用RTTI,只有当启动RTTI时,用来RTTI功能的typeid和dynamic_cast才能正常工作。

    2)type_info,用来描述类型信息。type_info存储了它所描述的类型的名字。RTTI就是使用type_info来实现的。type_info的定义如下:

    Code
    class type_info {
    public:
      
    virtual ~type_info();
      
    bool operator== (const type_info& rhs) const;
      
    bool operator!= (const type_info& rhs) const;
      
    bool before (const type_info& rhs) const;
      
    const char* name() const;
    private:
      type_info (
    const type_info& rhs);
      type_info
    & operator= (const type_info& rhs);
    };

    问题:RTTI怎么实现那?对象,type_info,虚函数怎么关联那?《深入C++对象模型》中说在虚函数表的开始存储了类型信息?经过高手指点确实是在虚函数表的前面还有一个指针来存储RTTICompleteObjectLocator,其中包含类型信息TypeDescriptor,类型继承信息RTTIClassHierarchyDescriptor类继承信息里包含了基类的信息RTTIBaseClassArray。可以参考:http://www.cppblog.com/dawnbreak/archive/2009/03/12/76354.html。


    3)typeid,在运行时获得对象的类型,typeid()返回的是const type_info&,而 type_info包含了对象真实类型的名字。typeid能被用来获取一个引用对象或指针指向的对象的运行时的真实类型。当然如果对象为null或编译时没有使用/GR的话,typeid的会抛出异常bad_typeid exception或__non_rtti_object。实例代码:

    Code
    class Base 
    {
    public:
        
    virtual void f(){ }
    };
     
    class Derived : public Base 

    public:
        
    void f2() {}
    }; 

    void main ()
    {
        Base 
    *pB = new Derived();
        
    const type_info& t = typeid(*pB);cout <<t.name() << endl;
        delete pB;

        Derived d;
        Base
    & b = d;
        cout 
    << typeid(b).name() << endl;
    }

    运行结果:

    4)dynamic_cast,用来运行时的类型转化,需要/GR来正确运行。
    适用:
    第一,用于所有的父子和兄弟间指针和引用的转化,有类型安全检查;
     
    第二,对指针类型,如果不成功,返回NULL,对引用类型,如果不成功,则抛出异常;
     
    第三,类型必须要有虚函数,且打开/GR编译选项,否则不能使用dynamic_cast。
    实例代码:

    Code
    class AA 
    {
    public:
        
    virtual void do_sth(){ std::cout<<"AA\n"; }
    };
    class BB 
    {
    public:
        
    virtual void do_sth(){ std::cout<<"BB\n"; }
    };
    class CC : public AA, public BB
    {
    public:
        
    virtual void do_sth(){ std::cout<<"CC\n"; } 
    };

    void DynamicCastTest()
    {
        AA 
    *pA = new CC;
        BB 
    *pB = dynamic_cast<BB*>(pA);
        
    if(pB != NULL)
            cout 
    << "cast successful!" << endl;
        CC 
    *pC = dynamic_cast<CC*>(pA);
        
    if(pC != NULL)
         cout 
    << "cast successful!" << endl;
    }

    二 其他cast

    1)隐式转化,不需要任何操作符,转化被自动执行,当一个值被赋值到它所兼容的类型时。
    适用:
    第一,内置基本类型的兼容转化;
    第二, 子类指针,引用向父类的转化;

    实例:

    Code
    class A
    {
    public:
        
    virtual ~A(){}
    };
    class B : public A
    {
    };

    void ImplicitCast()
    {
        
    short a = 2000;
        
    int b;
        b 
    = a;

        
    double d = 10.05;
        
    int i;
        i 
    = d;

        
    int j = 75;
        
    char c;
        c 
    = j;

        A
    * pA = new B();
    }

    2)强制类型转化,即我们常说的C风格的类型转化,基本上可以用于所有的转化,但是没有意义的转化除外,但是父子类,兄弟间的转化没有类型检查可能导致运行是错误。
    适用:
    第一,基本类型转化;
    第二,void*到其他指针的转化;
    第三,去除const;
    第五,函数指针的转化;
    第六,父子类转化,但是多重继承和兄弟转化,可能有运行时错误,没有类型检查;
    第七,任何两个类,但是没有实际意义,运行可能出错;
    第八,不能用于没有意义的转化,严厉禁止,例如,你不能用static_cast象用C风格的类型转换一样把struct转换成int类型,或者把double类型转换成指针类型;
    第九,在C++一般更推荐新加的static_cast,const_cast,dynamic_cast和reinterpret_cast转化方式;

    实例:

    Code
    class CDummy 
    {
    public:
        CDummy(
    float x, float y)
        {
            i 
    = x;
            j 
    = y;
        }
    private:
        
    float i,j;
    };

    class CAddition 
    {
    public:
        CAddition (
    int a, int b) { x=a; y=b; }
        
    int result() { return x+y;}
    private:
        
    int x,y;
    };

    int Testing()
    {
        std::cout 
    << "Testing" << std::endl;
        
    return 10;
    }

    void ExplicitCast()
    {
        
    double r = (double)1 / 3;

        
    int *pi = new int(10);
        
    void *pV;
        pV 
    = pi;
        
    int *pj = (int*)pV; // 或 int *pj = int*(pV);

        
    const int* pa = new int(20);
        
    int *pb;
        pb 
    = (int*)pa;
        
    *pb = 30;
        std::cout 
    << *pa << std::endl;

        typedef 
    void (*Fun)();

        Fun f 
    = (Fun)Testing;
        f();

        
    // 多重继承或将兄弟间的转化可能会出错

        
    // 虽然可以正确的编译,但是运行有问题,所以我们不做没有意义的转化
        
    //CDummy d(10,30);
        
    //CAddition * padd;
        
    //padd = (CAddition*) &d;
        
    //std::cout << padd->result();

        
    // 不做没有意义的转化
        //// error
        //struct st{int i; double d;};
        
    //st s;
        
    //int x = (int)s; //c2440

        
    //double y = 10.0;
        
    //int *p = (int*)y; // c2440
    }

    3)static_cast在功能上基本上与C风格的类型转换一样强大,含义也一样。
    它也有功能上限制:
    第一,不能兄弟间转化,父子间转化没有类型安全检查,有可能会导致运行时错误,父子兄弟的动态转化应该适用dynamic_cast;
    第二,不能去除const,适用专用的const_cast;
    第三,不能用于两个没有继承关系的类,当然实际上这样的转化也是没有意义的;
    第四,当然也不支持没有意义的转化,例如,你不能用static_cast象用C风格的类型转换一样把struct转换成int类型,或者把double类型转换成指针类型;

    4)const_cast,用来修改类型的const或volatile属性。

    适用:
    第一,常量指针被转化成非常量指针,并且仍然指向原来的对象;
    第二,常量引用被转换成非常量引用,并且仍然指向原来的对象;
    第三,常量对象被转换成非常量对象;

    实例:

    Code
    void ConstCastTest()
    {
        
    const int* pa = new int(20);
        
    int *pb;
        pb 
    = const_cast<int*>(pa);
        
    *pb = 30;
        std::cout 
    << *pa << std::endl;
    }

    5)reinterpret_cast,此转型操作符的结果取决于编译器,用于修改操作数类型,非类型安全的转换符。
    适用:
    一般不推荐使用,但是一般用来对函数指针的转化。
    实例:

    Code
    // 不可以移植,不推荐使用
    int ReinterpretTest()
    {
        
    struct dat { short a; short b;};
        
    long value = 0x00100020;
        dat 
    * pd = reinterpret_cast<dat *> (&value);
        std::cout 
    << pd-><< std::endl; // 0x0020
        std::cout << pd-><< std::endl; // 0x0010
        return 0;
    }

    typedef 
    void (*Fun)();

    int Testing()
    {
        std::cout 
    << "Testing" << std::endl;
        
    return 10;
    }

    void ReinterpretTest2()
    {
        
    //Fun f = (Fun)Testing;
        
    //f();
        Fun f = reinterpret_cast<Fun>(Testing);
        f();
    }

    三 总结

    在C++一般更推荐新加的static_cast,const_cast,dynamic_cast和reinterpret_cast转化方式;


    作者:iTech
    微信公众号: cicdops
    出处:http://itech.cnblogs.com/
    github:https://github.com/cicdops/cicdops

  • 相关阅读:
    自动化测试最新面试题和答案
    没有接口设计文档怎么做测试?
    测试岗/测试开发岗面经合集
    安卓测试常用的 ADB 命令
    面试问题集合
    springboot测试邮件发送
    swagger
    shiro 登录拦截和用户认证、资源授权
    SpringSecurity(安全框架)
    SpringBoot整合Mybatis框架
  • 原文地址:https://www.cnblogs.com/itech/p/1398230.html
Copyright © 2020-2023  润新知