【问题标题】:How to implement a virtual function equal() [duplicate]如何实现虚函数equal() [重复]
【发布时间】:2014-11-21 23:17:05
【问题描述】:

我想实现一个函数来比较派生类。但是我发现我必须将基类对象转换为派生类对象。

有没有办法避免在这里使用强制转换或其他更好的设计来实现比较功能?

class A {
    public :
        virtual bool equal(const A & obj) const = 0;
};

class AA : public A{
    public:
        AA (int i) : m_i(i) {}
        virtual bool equal(const A & obj) const
        {
            return m_i == dynamic_cast<const AA&>(obj).m_i;
        }

    private:
        int m_i;
};

int main () {
    AA aa1(10), aa2(9);
    A &a1 = aa1, &a2 = aa2;

    a1.equal(a2);

    return 0;
}

【问题讨论】:

  • 请注意,dynamic_cast 引用类型可能会引发异常。可能更好地转换为指针,如果指针返回 NULL,您可能只想返回 false
  • 可能在基类中为成员变量m_i使用了一个虚拟的getter?
  • 阅读有关双重调度及其经典解决方案 - 模式访问者的信息。
  • 如果 obj 不是 AA,您的代码将在动态转换中抛出异常。如果您改用指针,它会起作用。如何避免它取决于您如何使用 equals 和您的整体多态设计。
  • 在我看来,让相等操作考虑比程序员意识到的(静态类型)更多的信息(动态类型)是不好的。然而,虽然这在 C++ 中是不自然的,但据我所知,它已经在 J​​ava 中进行了彻底的研究。我会在那里寻找答案,如果我想要这里涉及的那种行为,非常动态的类型,我可能会从 C++ 切换到 Java。

标签: c++ inheritance


【解决方案1】:

我在自己的代码中多次被这个请求所困扰。我将提供一个简化的示例:

struct Fruit
{
  virtual bool is_equal(Fruit const & f) const = 0; // Compare two fruits
  // Some dangerous actions:
  bool operator==Fruit const & f)
  {
    return is_equal(f); // Dispatch!
  }
};

struct Strawberry : public Fruit
{
  bool is_equal(Fruit const & f)
  {
     bool equal = false;
     // The f could be any fruit, such as tomatoes or pineapples or bananas.
     // Need to perform a dynamic_cast to verify that the f is a strawberry.
     Strawberry const & s = dynamic_cast<Strawberry const &>(f);
     // perform strawberry comparison;
     return equal;
  };
}

struct Banana : public Fruit
{
  bool is_equal(Fruit const & f)
  {
     bool equal = false;
     // The f could be any fruit, such as tomatoes or pineapples or strawberries.
     // Need to perform a dynamic_cast to verify that the f is a banana.
     Banana const & b = dynamic_cast<Banana const &>(f);
     // perform banana comparison;
     return equal;
  };
}

bool Compare_Fruits(Fruit const * pf1, Fruit const * pf2)
{
  if (*pf1 == *pf2)
  {
    cout << "Fruits are equal\n";
    return true;
  }
  cout << "Fruits are different\n";
  return false;
}

int main(void)
{
  // Fun with fruit.
  Fruit * p_fruit_1 = new Strawberry;
  Fruit * p_fruit_2 = new Banana;
  Fruit * p_fruit_3 = new Strawberry;

  // Is this valid, comparing two different fruits, when
  // we just want to compare two strawberries?
  if (Compare_Fruits(p_fruit_1, p_fruit_3)) // OK, both are strawberries
  {
     // ...
  }
  if (Compare_Fruits(p_fruit_1, p_fruit_2)) // Not OK, two different fruits.
  {
     // ...
  }
  return 0;
}  

这里的重点是,如果您在基类中实现相等操作以便后代可以比较实例,那么您做出了一个错误的决定。请注意,我可以将指向一个后代(草莓)实例的指针传递给另一个后代(香蕉)的比较方法,因为相等函数基于指向基类的指针。不幸的是,没有办法知道有多少或所有派生自基类的后代。

为了编程安全,我强烈建议不要将虚拟比较运算符放在基类中。基类应该有比较运算符,声明为protected,只比较基本数据成员。这个方法会被后代调用。

【讨论】:

  • 那么,如果您有一对仅由基类指针或引用知道的对象,您会知道它们是否相等?
  • 如果基类指针或引用已知的两个对象只能使用基类的数据进行比较。只有当且仅当两个对象是同一类的实例时,您才能比较内容相等性。同样,当基类是 Life_Form 并且一个实例是树而另一个实例是蜘蛛时,正在比较什么?这没有意义。在我的项目中,Field 是一个基类,我有一个Record,它是Field 的容器。我如何确定我没有将 Field::TextField::Double 进行比较?
  • dynamic_cast 与问题中所示的虚拟比较函数一起使用,您可以轻松地将比较函数放在基类上 - 您不能在此处实现它。
猜你喜欢
  • 1970-01-01
  • 2015-11-30
  • 2011-09-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-02-26
  • 2010-09-11
  • 1970-01-01
相关资源
最近更新 更多