typeid 与 dynamic_cast(C++学习)

RTTI

Run-Time Type Information

运行时类型信息

Run-Time Type Identification

运行时类型识别

C++ 通过下面两个操作符提供RTTI功能

  • typeid

  • dynamic_cast

其中:

  • typeid 返回type_info类的对象的一个引用

  • typeid 会抛出bad_typeid的异常

  • dynamic_cast 会抛出bad_cast异常

这3个类在头文件 typeinfo 中:

namespace std{
class type_info;
class bad_cast;
class bad_typeid;
}

typeid 操作符

  • 操作符 typeid 用法上类似于操作符 sizeof (?)
  • 只有当 typeid 的参数是带虚函数的类类型的对象的时候,才返回动态类型信息.

    • 对其他,返回静态的、编译时的类型

例子:

#include 
#include 
#include 
#include 
using std::cout;
using std::endl;

int func(int arg){return 0;}
typedef int (*func_ptr)(int arg);

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

int main()
{
    int integer(1);
    double real(1.0);
    int array[10]={1,2,3};
    int * array_header = array;
    std::string string;
    std::vector int_vector;
    func_ptr f = func;

    cout<<"integer     : "<    cout<<"real        : "<    cout<<"array[10]   : "<    cout<<"array_header: "<    cout<<"std::string : "<    cout<<"std::vector : "<    cout<<"function    : "<    cout<<"function ptr: "<    cout<<"custom class: "<
    cout<
    cout<<"int         : "<    cout<<"double      : "<    cout<<"std::vector : "<).name()<    cout<<"Base        : "<    cout<<"Base*       : "<    cout<<"Base&       : "<
    return 0;
}

结果:

g++

MSVC

integer

i

int

real

d

double

array[10]

A10_i

int [10]

array_header

Pi

int *

std::string

Ss

classstd::basic_string
,classstd::allocator>

std::vector

St6vectorIiSaIiEE

classstd::vector>

function

FiiE

int__cdecl(int)

function ptr

PFiiE

int(__cdecl*)(int)

custom class

F4BasevE

classBase__cdecl(void)

int

i

int

double

d

double

std::vector

St6vectorIiSaIiEE

classstd::vector>

Base

4Base

class Base

Base*

P4Base

class Base *

Base&

4Base

class Base

对于每个类型,返回一个唯一的字符串,但该字符串的具体内容,与编译器实现有关。

在 typeid 中:

  • 数组不会退化为指针,比如int array[10]
  • 函数不会退化为指针, func 和 f
  • 顶层cv-qualifiers会被忽略 typeid(D) == typeid(const D);
  • ...

dynamic_cast 操作符

#include 
#include 

class Base1 { public: virtual void func1(){} };
class Base2 { public: virtual void func2(){} };

class D:public Base1, public Base2 {};

int main()
{
    Base1 * b1 = new D;
    Base2 & b2 =  dynamic_cast(*b1);

    std::cout << "Base1 " << uintptr_t(b1) <<" " << typeid(*b1).name() << std::endl
              << "Base2 " << uintptr_t(&b2) << " " <
    return 0;
}

结果:

MSVC

g++

Base1 208960 class D
Base2 208964 class D

Base1 208088 1D
Base2 208092 1D

C++0x 5.2.7:

  • The result of the expression dynamic_cast(v) is the result of converting the expression v to type T. T shall be a pointer or reference to a complete class type, or “pointer to cv void.”

dynamic_cast(v)中,T 是一个完整定义类的指针或引用,或者void*。只能转换到void*,不能从void*转换回来。

type_info 类

摘自 C++0x标准 18.7.1:

namespace std {
  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;
    size_t hash_code() const throw();
    const char* name() const;
    type_info(const type_info& rhs) = delete; // cannot be copied
    type_info& operator=(const type_info& rhs) = delete; // cannot be copied
  };
}

这里面,我们目前最感兴趣的是成员函数: name()

bad_typeid 类

异常的定义(来自C++0x 18.7.3):

namespace std {
  class bad_typeid : public exception {
    public:
    bad_typeid() throw();
    bad_typeid(const bad_typeid&) throw();
    bad_typeid& operator=(const bad_typeid&) throw();
    virtual const char* what() const throw();
  };
}

例子:

#include 
#include 

class Polymorphic {virtual void Member(){}};

int main ()
{
  try {
    Polymorphic * pb = 0;
    std::cout << typeid(*pb).name();
  } catch (std::bad_typeid& e) {
      std::cerr << "bad_typeid caught: " << e.what() << std::endl;
  }
  return 0;
}

结果:

MSVC

bad_typeid caught: Attempted a typeid of NULL pointer!

g++

bad_typeid caught: std::bad_typeid

异常产生条件:

  • 多态
  • 空指针
  • 用*解引用

比如,如果pb定义改为

int * pb = 0;

或 typeid 行改为

    std::cout << typeid(pb).name();

则不会有异常发生。

bad_cast 类

异常的定义(来自C++0x 18.7.2):

namespace std {
  class bad_cast : public exception {
    public:
    bad_cast() throw();
    bad_cast(const bad_cast&) throw();
    bad_cast& operator=(const bad_cast&) throw();
    virtual const char* what() const throw();
  };
}

例子:

#include 
#include 

class Base {virtual void Member(){}};
class Derived : Base {};

int main ()
{
  try {
    Base b;
    Derived& rd = dynamic_cast(b);
  } catch (std::bad_cast& bc) {
      std::cerr << "bad_cast caught: " << bc.what() << std::endl;
  }
  return 0;
}

结果:

MSVC

bad_cast caught: Bad dynamic_cast!

g++

bad_cast caught: std::bad_cast

  • 对指针cast失败,返回一个空指针
  • 对引用cast失败,则抛出bad_cast异常

参考

  • http://www.cplusplus.com/reference/std/typeinfo/bad_typeid

  • http://www.cplusplus.com/reference/std/typeinfo/bad_cast


你可能感兴趣的:(c/c++)