首页 > 代码库 > C++ Primer 学习笔记_101_特殊工具与技术 --运行时类型识别

C++ Primer 学习笔记_101_特殊工具与技术 --运行时类型识别

<style type="text/css">h2.western { font-family: "Liberation Sans",sans-serif; font-size: 16pt; }h2.cjk { font-family: "微软雅黑"; font-size: 16pt; }h2.ctl { font-family: "AR PL UMing CN"; font-size: 16pt; }h1 { margin-bottom: 0.21cm; }h1.western { font-family: "Liberation Sans",sans-serif; font-size: 18pt; }h1.cjk { font-family: "微软雅黑"; font-size: 18pt; }h1.ctl { font-family: "AR PL UMing CN"; font-size: 18pt; }p { margin-bottom: 0.25cm; line-height: 120%; }</style>

特殊工具与技术

--运行时类型识别



引:

通过下面两个操作符提供RTTI:

     1.typeid操作符,返回指针或引用所指对象的实际类型。

     2.dynamic_cast操作符,将基类类型的指针或引用安全地转换为派生类型的指针或引用。

   对于带虚函数的类,运行时执行RTTI操作符,但对于其他类型,在编译时计算RTTI操作符。

   当具有基类的引用或指针,但需要执行不是基类组成部分的派生类操作的时候,需要动态的强制类型转换。通常,从基类指针获得派生类行为最好的方法是通过虚函数。当使用虚函数的时候,编译器自动根据对象的实际类型选择正确的函数。

   但是,在某些情况下,不可能使用虚函数。在这些情况下,RTTI提供了可选的机制。然而,这种机制比使用虚函数更容易出错:程序员必须知道应该将对象强制转换为哪种类型,并且必须检查转换是否成功执行了



一、dynamic_cast操作符

   可以使用dynamic_cast操作符将基类类型对象的引用或指针转换为同一继承层次中其他类型的引用或指针。与dynamic_cast一起使用的指针必须是有效的—— 它必须为 0或者指向一个对象

   与其他强制类型转换不同,dynamic_cast涉及运行时类型检查。如果绑定到引用或指针的对象不是目标类型的对象,dynamic_cast失败。如果转换到指针类型dynamic_cast失败,dynamic_cast的结果是 0;如果转换到引用类型dynamic_cast失败,则抛出一个bad_cast类型的异常

1、使用dynamic_cast操作符

    if (Derived *derivedPtr = dynamic_cast<Derived *>(basePtr))
    {
        //...
    }
    else
    {
        //...
    }

<style type="text/css">p { margin-bottom: 0.25cm; line-height: 120%; }</style>

解释:在运行时,如果basePtr实际指向Derived对象,则转换将成功,并且derivedPtr将被初始化为指向basePtr所指的Derived对象;否则,转换的结果是0,意味着将derivedPtr置为0,并且if中的条件失败。同时也可以对值为 0的指针应用dynamic_cast,这样做的结果是0

【最佳实践】

  在条件中执行dynamic_cast保证了转换和其转换结果测试在一个表达式中进行!



2、使用dynamic_cast和引用类型

形式:

    dynamic_cast<Type &>(val);

<style type="text/css">p { margin-bottom: 0.25cm; line-height: 120%; }</style>

   只有val实际引用的是一个Type类型对象,或者val是一个Type派生类型的对象的时候,dynamic_cast操作才将操作数val转换为Type&类型

  因为不存在空引用,所以不可能对引用使用用于指针强制类型转换的检查策略,相反,当转换失败的时候,它抛出一个std::bad_cast异常,该异常在库头文件typeinfo中定义。

void f(const Base &b)
{
    try
    {
        const Derived &d = dynamic_cast<Derived &>(b);
    }
    catch(std::bad_cast)
    {

    }
}

//P648 习题18.13-15
//1)
class A
{
public:
    virtual void func()
    {}
};
class B : public A {};
class C : public B {};
class D : public B,public A {};

int main()
{
    C c;
    A *pa = &c;
    if (C *pc = dynamic_cast<C *>(pa))
    {
        cout << "dynamic_cast success" << endl;
    }
    else
    {
        cout << "dynamic_cast fail" << endl;
    }
}

//2)
    try
    {
        C &temp = dynamic_cast<C &>(*pa);
    }
    catch(std::bad_cast &e)
    {
        cout << e.what() << endl;
    }

//18.16 什么时候可以使用dynamic_cast代替虚函数
/**答:
*当我们需要在派生类中增加函数,但又不能在基类增加虚函数时
*就可以使用dynamic_cast代替 虚函数
*/

<style type="text/css">p { margin-bottom: 0.25cm; line-height: 120%; }</style>



二、typeid操作符

   typeid操作符使程序能够问一个表达式:你是什么类型?

	  typeid(e) 

<style type="text/css">p { margin-bottom: 0.25cm; line-height: 120%; }</style>

   如果表达式的类型是类类型且该类包含一个或多个虚函数,则表达式的动态类型可能不同于它的静态编译时类型。例如,如果表达式对基类指针解引用,则该表达式的静态编译时类型是基类类型;但是,如果指针实际指向派生类对象,typeid操作符将说表达式的类型是派生类型。

   typeid操作符可以与任何类型的表达式一起使用。内置类型的表达式以及常量都可以用作typeid操作符的操作数。如果操作数不是类类型或者是没有虚函数的类,typeid操作符指出操作数的静态类型;如果操作数是定义了至少一个虚函数的类类型,则在运行时计算类型

  typeid操作符的结果是名为type_info的标准库类型的对象引用,要使用type_info,必须包含库头文件typeinfo


使用typeid操作符

   typeid最常见的用途是比较两个表达式的类型,或将表达式的类型与特定类型相比较:

    Base *pb;
    Derived *pd;

    if (typeid(*pb) == typeid(*pd))
    {
        cout << "typeid(*pb) = typeid(*pd)" << endl;
    }

    if (typeid(pb) == typeid(pd))
    {
        cout << "typeid(pb) = typeid(pd)" << endl;
    }

    if (typeid(*pb) == typeid(Derived))
    {
        cout << "typeid(*pb) = typeid(Base)" << endl;
    }

    int a;
    if (typeid(int) == typeid(a))
    {
        cout << "typeid(int) = typeid(a)" << endl;
    }

<style type="text/css">p { margin-bottom: 0.25cm; line-height: 120%; }</style>

【小心地雷】

   只有当typeid的操作数是带虚函数的类类型的对象的时候,才返回动态类型信息。测试指针(相当于指针指向的对象)返回指针的静态的、编译时类型。

    Derived d;
    Base *pb = &d;

    if (typeid(*pb) == typeid(Derived))
    {
        cout << "typeid(*pb) = typeid(Base)" << endl;
    }


<style type="text/css">p { margin-bottom: 0.25cm; line-height: 120%; }</style>

如果指针pb的值是 0,那么,如果pb的类型是带虚函数的类型,typeid(*p)抛出一个bad_typeid异常;如果p的类型没有定义任何虚函数,则结果与 p的值是不相关的。


//P650 习题18.17
    AndQuery a;
    Query_base *qb1 = &a,*qb2;

    if (dynamic_cast<AndQuery *>(qb1))
        cout << "success" << endl;
    else
        cout << "failure" << endl;

    if (dynamic_cast<AndQuery *>(qb2))
        cout << "success" << endl;
    else
        cout << "failure" << endl;

// 习题18.18
    try
    {
        dynamic_cast<AndQuery &>(*qb1);
        cout << "success" << endl;
    }
    catch(std::bad_cast)
    {
        cout << "failure" << endl;
    }

    try
    {
        dynamic_cast<AndQuery &>(*qb2);
        cout << "success" << endl;
    }
    catch(std::bad_cast)
    {
        cout << "failure" << endl;
    }

//习题18.19
    AndQuery a;
    Query_base *qb1 = &a,*qb2;

    try
    {
        dynamic_cast<AndQuery &>(*qb1);

        if (typeid(*qb1) == typeid(AndQuery))
        {
            cout << "Same" << endl;
        }
        else
        {
            cout << "Not Same" << endl;
        }
    }
    catch(std::bad_cast)
    {
        cout << "failure" << endl;
    }

    if (typeid(*qb1) == typeid(*qb2))
    {
        cout << "Same" << endl;
    }
    else
    {
        cout << "Not Same" << endl;
    }

C++ Primer 学习笔记_101_特殊工具与技术 --运行时类型识别