实现RTTI的两个重要运算符:
typeid:返回表达式的类型。
dynamic_cast:用于将基类的指针或引用安全地转换成派生类的指针或引用。
当我们将这两个运算符用于某种类型的指针或引用,并且该类型含有虚函数时,运算符将使用指针或者引用所绑定对象的动态类型。
特别适用于此情况:我们想使用基类对象的指针或引用执行某个派生类操作并且该操作不是虚函数。
1.dynamic_cast:比如含有虚函数的基类B和从基类B派生出的派生类D,则B *pb; D *pd, d; pb=&d; pd=dynamic_cast<D*>(pb); 最后一条语句表示把指向派生类D的基类指针pb转换为派生类D的指针,然后将这个指针赋给派生类D的指针pd,有人可能会觉得这样做没有意义,既然指针pd要指向派生类为什么不pd=&d;这样做更直接呢?因为虚函数的基类版本和派生类版本必须具有相同的形参类型。就是当在派生类中覆盖虚函数时,若参数有基类指针或引用,需在函数里将参数强制转换为派生类指针或引用。也即有些时候我们需要强制转换,比如如果指向派生类的基类指针B想访问派生类D中的除虚函数之外的成员时就需要把该指针转换为指向派生类D的指针,以达到访问派生类D中特有的成员的目的,比如派生类D中含有特有的成员函数g(),这时可以这样来访问该成员dynamic_cast<D*>(pb)->g();因为dynamic_cast转换后的结果是一个指向派生类的指针,所以可以这样访问派生类中特有的成员。但是该语句不影响原来的指针的类型,即基类指针pb仍然是指向基类B的。如果单独使用该指针仍然不能访问派生类中特有的成员。
2.typeid:先判断对象类型,才能确保dynamic_cast进行安全转换。
<strong>//例:比如我们有两个变量,它们可能是基类对象也可能是派生类对象。我们要比较它们是否相等要怎么做?首先,我们必须用typeid看这两个对象是否同类型!然后才对它们的每个属性进行比较(这又涉及到另一个问题:虚函数的基类版本和派生类版本必须具有相同的形参类型。如果我们想定义一个虚函数equal,则该函数的形参必须是基类的指针或引用。此时,equal函数将只能使用基类的成员,而不能比较派生类独有的成员。当在派生类中覆盖equal时会出现这个问题)!</strong>
class Base
{
friend bool operator==(const Base&,const Base&);
public:
//Base的接口成员
protected:
virtual bool equal(const Base&)const;
//Base的数据成员和其他用于是实现的成员
};
class Derived:public Base
{
public:
//Derived的其他接口成员
protected:
bool equal(const Base&)const; //需强制转换,否则无法比较Derived的特有成员!
//Derived的数据成员和其他用于实现的成员
};
bool operator==(const Base &lhs,const Base &rhs)
{
//如果typeid不相同,返回false;否则虚调用equal
return typeid(lhs)==typeid(rhs)&&lhs.equal(rhs); //动态绑定!
}
bool Derived::equal(const Base &rhs)const
{
//我们清楚这两个类型是相等的,所以转换过程不会抛出异常
auto r=dynamic_cast<const Derived&>(rhs); //在派生类中覆盖equal,需强制转换,因为基类引用是无法取得派生类的特有成员的!
//执行比较两个Derived对象的操作并返回结果
}
bool Base::equal(const Base &rhs)const
{
//执行比较Base对象的操作
}