有下面一个继承体系,我们需要为其添加"=="操作符,该怎么办呢 ??
class Animal { }; class Cat : public Animal { }; class Dog : public Animal { };
如果我们为每个类重载"=="运算符,那么我们还需要重载子类与基类之间的"=="运算符,这样代码实现起来是很恐怖的。
我们可以尝试,只为基类重载一个"=="运算符,然后在基类中实现一个虚函数equal,在子类中去实现它。
实现代码如下:
1 #include <iostream> 2 #include <typeinfo> 3 #include <string> 4 using namespace std; 5 6 class Animal 7 { 8 public: 9 virtual bool equal(const Animal &other) const = 0; 10 }; 11 12 class Cat : public Animal 13 { 14 public: 15 bool equal(const Animal &other) const 16 { 17 //Cat Animal 18 if(const Cat *pc = dynamic_cast<const Cat*>(&other)) 19 { 20 return name_ == pc->name_; 21 } 22 return false; 23 } 24 25 private: 26 string name_; 27 }; 28 29 class Dog : public Animal 30 { 31 public: 32 bool equal(const Animal &other) const 33 { 34 //Dog Animal 35 if(const Dog *pd = dynamic_cast<const Dog*>(&other)) 36 { 37 return name_ == pd->name_; 38 } 39 return false; 40 } 41 private: 42 string name_; 43 }; 44 45 bool operator==(const Animal &a, const Animal &b) 46 { 47 return typeid(a) == typeid(b) && a.equal(b); 48 } 49 50 int main(int argc, char const *argv[]) 51 { 52 53 Cat c; 54 Dog d; 55 56 Animal *pa = &c; 57 cout << (*pa == c) << endl; 58 pa = &d; 59 cout << (*pa == d) << endl; 60 return 0; 61 }
在重载操作中:
bool operator==(const Animal &a, const Animal &b) { return typeid(a) == typeid(b) && a.equal(b); }
typeid是一种类型识别运算符,如果要识别的类型不是class或不含有virtual函数,那么在编译期间自动识别,typeid指出静态类型,如果class中含有virtual函数,那么typeid在运行期间识别类型。
对equal的调用,显然使用了动态识别,总是能根据对象的实际类型,调用对应的equal版本。
在equal函数中,dynamic_cast使得对象进行了"向下塑形",static_cast与dynamic_cast之间有一些不同:
static_cast发生在编译期间,如果转化不通过,那么编译错误,如果编译无问题,那么转化一定成功。static_cast仍具有一定风险,尤其是向下塑形时,将Base*转化为Derived*时,指针可以转化,但是指针未必指向Derived对象。
dynamic_cast发生在运行期间,用于将Base的指针或者引用转化为派生类的指针或者引用,如果成功,返回正常的指针或引用,如果失败,返回NULL(指针),或者抛出异常(bad_cast)。
在"=="的重载中,我们保证了equal两个参数的类型相同,那么我们为何还需要在equal中“向下塑形”呢?
equal有可能被单独使用,所以other的类型未必和自己相同。
如果不进行转换,other是无法访问name属性的,因为Animal中没有name。
typeid和dynamic_cast是实现RTTI的主要手段。