回顾cpp-多态-RTTI

RTTI的主要内容

RTTI:运行时类型识别。主要有两个运算符实现:

  • typeid: 返回对象
  • dynamic_cast: 用于将父类的指针或引用安全地转化成子类的指针或引用

为什么设计RTTI

当处于这种情况时,使用RTTI:当我们想使用基类对象的指针或引用,执行某个派生类的操作,并且该操作不是虚函数时。

一般讲,只要有可能,我们应该尽量使用虚函数。当一个方法被定义成虚函数时,编译器将根据对象的动态类型自动正确地选择正确的方法版本。

但,并不是任何时候都能定义虚函数的。当无法使用虚函数时,就使用RTTI。另一方面,使用RTTI,程序员必须清楚地知道要转换成什么类型,并且检查转换是否成功。

dynamic_cast 使用规则:

  • 只能使用对象指针对象引用的转换:dynamic_cast<bird*>或dynamci_cast<&bird>,而不能是对象本身。
  • 需要转换的类型中必须包含虚函数
  • 如果转换成功返回子类地址,否则返回NULL。

typeid 使用规则:

  • type_id 返回一个type_info对象的引用。
  • 如果想通过基类指针获得派生类的数据类型,基类必须带有虚函数
  • 只能判断当前对象是基类还是子类,无法判断指针是基类还是子类。即typeid()参数是对象,而非指针。

type_info 中的内容:

1
2
3
4
5
6
7
8
9
10
class type_info{
public:
const char* name() const;
bool operator==(const typ_info& rhs) const;
bool operator!=(const typ_info& rhs) const;
int before(const type_info& rhs) const;
virtual ~type_info();
private:
......
};

用法与实例

假如由两个类都继承了借口类Flyable:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Flyable{
public:
virtual void takeoff()=0;
virtual void landing()=0;
};


class Bird : public Flyable{
public:
// foraging()方法不是虚函数
void foraging(){cout<<"bird--foraging"<<endl;}
virtual void takeoff(){cout<<"bird--takeoff"<<endl;}
virtual void landing(){cout<<"bird--landing"<<endl;}
};


class Plane : public Flyable{
public:
// carry()方法不是虚函数
void carry(){cout<<"plane--carray"<<endl;}
virtual void takeoff(){cout<<"plane--takeoff"<<endl;}
virtual void landing(){cout<<"plane--landing"<<endl;}
};

Bird由飞行能力,且可以觅食,Plane有飞行能力,且可以运输。

可以定义一个函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
void func(Flyable* obj){
// 1)
cout<< typeid(*obj).name()<<endl;
obj->takeoff();

// 2)
if(typeid(*obj)== typeid(Bird)){

// 3)
Bird* bird = dynamic_cast<Bird*>(obj);
bird->foraging();
}
// 4)
if(typeid(*obj)== typeid(Plane)){
Plane* plane = dynamic_cast<Plane*>(obj);
plane->carray();
}

obj->landing();
}

1)打印obj是什么类型。typeid(对象):参数是obj所指向的对象
2)判断当前obj指针所指向的是什么类型。如果obj指向的对象是Bird类型,
3)将原本是Flyable 类型obj指针,cast为Bird 类指针。并且将转化后的指针复制给一个新的指针。
4)同3)。

此时在main中调用func()。

1
2
3
Bird b ;
func(&b);
cout<< typeid(b).name()<<endl; //

b为Bird类

1
2
3
4
Flyable* plane = new Plane();
func(plane);
cout<< typeid(plane).name()<<endl; //
cout<< typeid(*plane).name()<<endl; //

分别返回Flyable 指针类,和Plane 类

typeid().name()使用前需要#include <typeinfo>

敲黑板在可能的情况下,最好定义虚函数,而非直接管理类型。