RTTI,run-time type identification。

动态类型:也叫运行时类型,指的是变量直到运行时,才能确定它的类型。
静态类型:指的是变量在编译时就能确定的类型。

由两个运算符实现:

  • typeid:返回表达式的类型
  • dynamic_cast:将基类指针、引用转换成派生类的指针、引用,我们常说的动态类型转换。 ```cpp

Derived d; Base p = &d; // p的静态类型是Base,动态类型是Derived*。

void fuck(Base p){ // p运行时指向的是派生类对象,我们想调用派生类特有的成员,这时候就需要将 // 基类的指针引用转换成派生类的指针引用进行操作了。 if(Derived pShit = dynamic_cast(p)){ pShit->DerivedMethod(); // 调用派生类的方法。 } else{} // 转换失败,因为p运行时指向的不是Base或者它派生类的对象。 }

  1. <a name="pZgR1"></a>
  2. # dynamic_cast
  3. 将基类的**指针**、**引用**转换成派生类的指针、引用。
  4. ```cpp
  5. /*****************dynamic_cast使用形式***********************/
  6. // type必须是类类型。
  7. dynamic_cast<type*> (e); // 指针形式,e必须是指针,否则转换必定失败。
  8. dynamic_cast<type&> (e); // 左值引用,e必须是左值引用,否则转换必定失败。
  9. dynamic_cast<type&&> (e); // 右值引用,e不能是左值引用,否则转换必定失败。
  10. // e的类型必须满足以下条件之一,才可能转换成功
  11. // 1、e的类型是type类型的子类。
  12. // 2、e的类型是type类型的共有基类。
  13. // 3、e的类型就是type的类型。
  14. // 转换失败时,会发生2中行为:
  15. // 1、如果是转换指针形式,则返回0,也就是空指针。
  16. // 2、如果是转换引用形式,则抛出异常std::bad_cast,
  17. // 我们应该通过try catch捕获异常来判断是否成功
  18. /*****************dynamic_cast使用形式***********************/
  19. // 指针形式的使用例子
  20. if(Derived *pShit = dynamic_cast<Derived*>(p)){
  21. pShit->DerivedMethod(); //调用派生类的方法。
  22. }
  23. else{} //转换失败,返回空指针
  24. // 不建议这么使用指针形式,
  25. Derived *pShit = dynamic_cast<Derived*>(p);
  26. if(pShit){}
  27. // 引用形式的例子
  28. try {
  29. const Derived &d = dynamic_cast<const Derived&>(b);
  30. // 使用b引用的 Derived 对象
  31. }
  32. catch (bad_cast) {} // 处理类型转换失败的情况

typeid

返回表达式的类型信息,可能是动态类型,可能是静态类型。

  1. /*****************typeid使用形式***********************/
  2. // typeid运算符的使用形式
  3. // e:任意表达式、或者是类型的名字
  4. // 返回type_info或者它的派生类对象的常量引用
  5. // type_info是类类型,定义在头文件typeinfo中,在下面有介绍
  6. const type_info& info = typeid(expr); // 任意表达式expr
  7. const type_info& info = typeid(T); // 类型名字T
  8. // e是引用,返回所引用对象的类型
  9. // e是数组、函数,返回数组、函数类型,不是自动转换成指针类型。
  10. // e不是类类型,返回静态类型
  11. // e的类类型不包含任何虚函数,返回静态类型
  12. // e是左值,且是有虚函数的类类型时,返回动态类型。
  13. // 当时返回动态类型时,会对表达式求值
  14. // 当时返回静态类型时,不对表达式求值
  15. /*****************typeid使用例子***********************/
  16. Derived *dp = new Derived;
  17. Base *bp = dp;
  18. // 在运行时比较两个对象的类型
  19. // 两个指针都指向Derived对象
  20. if(typeid(*bp) == typeid (*dp)) {
  21. // bp和dp指向同一类型的对象
  22. }
  23. // 检查运行时类型是否是某种指定的类型
  24. if(typeid(*bp) == typeid(Derived)){
  25. // bp实际指向Derived对象
  26. }
  27. //注意!!!这种检查,永远是失败的,前者返回的永远是指针类型Base*
  28. if (typeid(bp) == typeid(Derived)) {
  29. // 此处的代码永远不会执行
  30. }

type_info

C++标准规定了type_info必须包含的操作,而不同编译器可能会添加额外的操作,

  1. t1 == t2; // 如果type_info对象t1和t2表示同一种类型,返回true;否则返回false
  2. t1 != t2; // 如果type_info对象t1和t2表示不同的类型,返回true;否则返回false
  3. t.name(); // 返回一个C风格字符串,表示类型名字的可打印形式。编译器相关。
  4. t1.before(t2); // 返回一个bool值,表示t1是否位于t2之前。
  5. // before所采用的顺序关系是依赖于编译器的
  6. // type_info没有默认构造函数,拷贝控制成员都是delete的。
  7. // 只能通过typeid返回typeinfo

例子

在派生体系中实现一个equal函数用于判断类对象是否相等(类型相同且所有成员数值相同)。

  1. class Base{
  2. friend bool operator==(const Base &,const Base &);
  3. public:
  4. // Base的接口成员
  5. protected:
  6. virtual bool equal(const Base &) const;
  7. // Base的数据成员和其他用于实现的成员
  8. }
  9. class Derived: public Base{
  10. public:
  11. // Derived的其他接口成员
  12. protected:
  13. bool equal(const Base &r) const{
  14. // 我们清楚这两个类型是相等的,所以转换过程不会抛出异常
  15. auto r = dynamic_cast<const Derived&>(rhs);
  16. // 执行比较两个Derived对象的操作并返回结果
  17. ......
  18. }
  19. // Derived的数据成员和其他用于实现的成员
  20. }
  21. bool operator==(const Base &lhs , const Base &rhs){
  22. // 如果typeid不相同,返回false; 否则虚调用equal
  23. return typeid(lhs) == typeid(rhs) && lhs.equal(rhs);
  24. }