C++——使用RTTI为继承体系编写"=="运算符

有下面一个继承体系,我们需要为其添加"=="操作符,该怎么办呢 ??

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的主要手段。

时间: 2024-08-11 18:03:23

C++——使用RTTI为继承体系编写"=="运算符的相关文章

C++ 继承体系中的名称覆盖

首先一个简单的样例: int x; int f() { double x; cin >> x; return x; } 在上述代码中.函数f的局部变量x掩盖了全局变量x.这得从 "C++的名字查找机制"说起,首先.在函数f的局部作用域中查找.(甚至是语句块).假设不存在,到上一层的作用域再进行查找,... 该命名空间中查找,最后是全局作用域. 在类的继承体系中,名字覆盖问题也是非常困扰的.此处的"名字"但是是函数名,变量名,typedef, enum都能

JAVA设计模式-装饰设计模式-继承体系的由来和装饰类的优化

首先看一下我们设计类的过程: 专门用于读取数据的类 MyReader l--MyTextReader:根据不同的功能会不断延伸很多子类 l--MyMeidaReader l--MyDataReader l--.......... 为了提高以上子类的工作效率,需要加入缓冲区技术.所以又会出现下面的类的继承体系. (体系1) MyReader l--MyTextReader:根据不同的功能会不断延伸很多子类 l--MyBufferedTextReader l--MyMeidaReader l--My

跟王老师学异常(三)异常类的继承体系

异常类的继承体系 主讲人:王少华  QQ群号:483773664 学习目标: 1.掌握异常的体系 2.掌握处理异常类的几中常用方法 一.异常类继承体系图 Java提供了丰富的异常类,这些异常类之间有严格的继承关系,如下图所示 从上图可以看出,Java把所有非正常情况分成两种,一种是异常(Exception),另一种是错误(Error),它们都继承Throwable父类. 二.Error Error错误,一般是指虚拟机相关的问题.即仅靠程序本身无法恢复的严重错误.如系统崩溃.虚拟机出错误.动态链接

Scala 深入浅出实战经典 第41讲:List继承体系实现内幕和方法操作源码揭秘

package com.parllay.scala.dataset /** * Created by richard on 15-7-25. * 第41讲:List继承体系实现内幕和方法操作源码揭秘 */object List_Interal { def main(args: Array[String]) { /** * List: 继承体系: * list有两个子类 Nil, ::, 他们都实现了 * override def head : B = hd override def tail :

[py]python的继承体系

python的继承体系 python中一切皆对象 随着类的定义而开辟执行 class Foo(object): print 'Loading...' spam = 'eggs' print 'Done!' class MetaClass(type): def __init__(cls, name, bases, attrs): print('Defining %s' % cls) print('Name: %s' % name) print('Bases: %s' % (bases,)) pri

关于Java继承体系中this的表示关系

Java的继承体系中,因为有重写的概念,所以说this在子父类之间的调用到底是谁的方法,或者成员属性,的问题是一个值得思考的问题; 先说结论:如果在测试类中调用的是子父类同名的成员属性,这个this.成员  属性的值就是父类中这个成员属性的值,因为在Java中没有重写成员属性这一概念 ,如果调用的是同名的成员方法的话,那就应该调用的是重写后的成员方法.....如果子类中没有这个成员方法,则向父类中寻找. 上代码支持论证: package Test_This; public class Demo

spring的beanFactory继承体系

一.BeanFactory 是 Spring 的“心脏”.它就是 Spring IoC 容器的真面目.Spring 使用 BeanFactory 来实例化.配置和管理 Bean. BeanFactory:是IOC容器的核心接口, 它定义了IOC的基本功能,我们看到它主要定义了getBean方法.getBean方法是IOC容器获取bean对象和引发依赖注入的起点.方法的功能是返回特定的名称的Bean. BeanFactory 是初始化 Bean 和调用它们生命周期方法的“吃苦耐劳者”.注意,Bea

Request继承体系

ServletRequest——接口 ↑继承 HttpServletRequest——接口 ↑实现 org.apache.catalina.connector.RequestFacade——类(Tomcat编写的) 通过RequestFacade类,创建Request对象,传递给service方法 在源码包的位置java/org/catalina/connector/RequestFacade java专门用来放代码的 RequestFacade是将来要是用的,看api的调用就只需要看HttpS

Java之访问权限控制符以及结合继承体系引发的注意事项

访问修饰符的作用域: private:可以定义方法或者属性,定义的方法和属性不能被外部的类所访问(包括子类). default:可以在本包中的任意地方访问. protected:保护,不同包中的非子类不能访问.(能访问的成员包括相同包以及该类的任意子类) public:公共的,都可以访问,不受任何限制. Java继承&访问修饰符结合使用引发的大混乱 1.子类重载方法的权限是否允许小于父类定义?其中,private<default<public 不允许,注:Java中允许Parent