RTII
运行时类型识别即RTTI主要由以下两个运算符实现
- typeid,返回表达式类型
- dynamic_cast,将基类指针或引用安全地转换成派生类指针或引用
运算符会自动使用指针或引用对象的动态类型。特别适用于想用基类指针或引用来执行派生类的不是虚函数的操作,即无法使用虚函数的情况。但实际上尽可能定义虚函数而非直接接管类型管理。
RTTI的实现主要是靠虚函数表之前的类型信息。
dynamic_cast
使用形式有
- dynamic_cast<type*>(e)
- dynamic_cast<type&>(e)
- dynamic_cast<type&&>(e)
要求e的类型必须是type的公有派生类(向上转型)、公有基类(向下转型)或者type类型之一。如果e是指针,且转换失败,返回空指针;如果e是引用,且转换失败,抛出bad_cast异常。
指针类型:对于向下转型,如果e指向type对应的派生类,返回指向该派生类的指针;否则返回0。可以简单通过判断返回值是否为0来判断转换是否成功。
引用类型:对于向下转型,如果e引用的是type对应的派生类,返回指向该派生类的引用;否则抛出bad_cast异常。可以通过try-catch来判断转换是否成功。
typeid
可以用于任意类型的表达式,忽略顶层const,返回一个type_info对象,包含表达式的类型信息。
可以通过该函数比较两个表达式类型是否相同,注意应该作用于对象。
只有类型含有虚函数时,才会对表达式求值,否则直接返回表达式的静态类型,因为编译器知道。
一种使用情况
如何为具有继承关系的类实现相等运算符?通常来说相等意味着类型相同且成员取值相同。
如果定义一个虚函数,在继承体系的各个层次上分别实现,可以吗?不可以,因为虚函数的形参类型必须相同,而不同层次的参数类型不同,导致函数内只能比较共有成员。
因此可以使用RTTI,先使用typeid判断类型是否相同,如果相同再使用dynamic_cast转换成对应类型,再比较成员取值。
#include<iostream>
#include<typeinfo>
using namespace std;
class Base{
friend bool operator==(const Base&, const Base&);
public:
Base(int i = 0):a(i){}
private:
int a;
protected:
virtual bool equal(const Base& bs) const{
return a == bs.a;
}
};
class Derived: public Base{
public:
Derived(int i = 0,int j = 0):Base(i),b(j){}
private:
int b;
protected:
bool equal(const Base& bs) const{
const Derived* p = dynamic_cast<const Derived*>(&bs);
return Base::equal(bs) && b == p->b;
}
};
Base* getBase(Base* p ){
cout << "in typeid" << endl;
return p;
}
bool operator==(const Base& lhs, const Base& rhs){
return typeid(lhs) == typeid(rhs) && lhs.equal(rhs);
}
class A{
};
A* getBase(A* p){
cout << "in A*" << endl;
return p;
}
int main()
{
Base b1(1), b2(2);
Derived d1(1, 2), d2(1, 2);
A a;
cout << typeid(*getBase(&b1)).name()<<endl;
cout << typeid(*getBase(&a)).name()<<endl;
cout << (b1 == b2) << endl;
cout << (d1 == d2) << endl;
cout << (b1 == d1) << endl;
cout << (d1 == b1) << endl;
return 0;
}
/*
in typeid
4Base
1A
0
1
0
0
*/