【问题标题】:Equality Test for Derived Classes in C++ [duplicate]C ++中派生类的相等性测试[重复]
【发布时间】:2010-12-18 10:02:10
【问题描述】:

可能重复:
What’s the right way to overload operator== for a class hierarchy?

在 C++ 中,派生类如何以有意义的方式覆盖基类相等性测试?

例如,假设我有一个基类 A。类 B 和 C 派生自 A。现在给定两个指向两个 A 对象的指针,我可以测试它们是否相等(包括任何子类数据)?

class A {
    public: int data;
};

class B : public A {
    public: float more_data; bool something_else;
};

class C : public A {
    public: double more_data;
};


    A* one = new B;
    A* two = new B;
    A* three = new C;

    //How can I test if one, two, or three are equal
    //including any derived class data?

有干净的方法吗?我最好的选择是什么?

谢谢!

【问题讨论】:

  • 您想比较T==T,其中T 可能是ABC(很好),或者您想比较ABACBC(有问题)?
  • 在上面的例子中,我想比较一与二和三​​。它们都是A指针。
  • @Imbue:那么你想要的就没有意义了。因为只有Bsomething_else 它永远不能等于AC。这种比较的语义是什么?
  • @sbi:我不是要求将 B 与 A 或 B 与 C 进行比较。我要求将两个 As 一起比较。在我上面的例子中,一、二、三都是 A*。

标签: c++ inheritance operators equality equals-operator


【解决方案1】:

我记得读过关于 public-non-virtual/non-public-virtual 习语及其优点的简洁描述,但没有在哪里。 This wikibook 的描述还可以。

这是你如何将它应用到 op==:

struct A {
  virtual ~A() {}

  int a;

  friend
  bool operator==(A const& lhs, A const& rhs) {
    return lhs.equal_to(rhs);
  }
  // http://en.wikipedia.org/wiki/Barton-Nackman_trick
  // used in a simplified form here

protected:
  virtual bool equal_to(A const& other) const {
    return a == other.a;
  }
};

struct B : A {
  int b;

protected:
  virtual bool equal_to(A const& other) const {
    if (B const* p = dynamic_cast<B const*>(&other)) {
      return A::equal_to(other) && b == p->b;
    }
    else {
      return false;
    }
  }
};

struct C : A {
  int c;

protected:
  virtual bool equal_to(A const& other) const {
    if (C const* p = dynamic_cast<C const*>(&other)) {
      return A::equal_to(other) && c == p->c;
    }
    else {
      return false;
    }
  }
};

【讨论】:

    【解决方案2】:

    不同的派生类可以产生相同的对象吗?

    如果是这样:double dispatch 是一个选项:它确实需要在基类中重载,所以你会有依赖项

    如果不是:解决方案是在 operator==() 中检查 typeid 并在它们不同时返回 false。否则调用一个私有的 equal() 函数,派生类可以在其中进行 static_cast 和比较。

    bool base::operator==(const base& other) const
    {
      if (typeid(*this) != typeid(other)) return false;
      return equal(other);
    }
    
    bool derived::equal(const base& other) const
    {
      derived& derOther = static_cast<derived&>(other);
      // compare derOther with *this
      return true;  // there is nothing to compare
    }
    

    这避免了所有派生类中的类型检查

    【讨论】:

    • 这可以防止使用 B 的派生类(例如 B2)与 B 进行比较。(使用问题中的层次结构。)
    • operator==() 只定义在基类中,所以可以使用层次结构。 equal() 函数必须是私有的(如前所述),并且只能由 operator==() 调用
    【解决方案3】:

    这样做的一种方法是使用virtual operator==,它将基类对象作为参数,以便它与不同的派生对象正常工作。但是,您需要使该函数成为纯虚拟函数,以便强制所有派生对象实现它。因此,您将无法实例化基类。例如:

    class A
    {
    public:
        virtual ~A(){}
    
        //A virtual operator for comparison
        virtual bool operator==(const A& a) = 0;
    
    protected:
        bool compareBase(const A& a);
    
    private:
        int m_data;
    };
    
    bool A::compareBase(const A &a)
    {
        return m_data == a.m_data;
    }
    
    class B1 : public A
    {
    public:
    
        //Override the base class
        bool operator==(const A& a);
    
    private:
        bool compare(const B1* pB)
        {
            if(compareBase(*pB))
            {
                //Code for compare
                return true;
            }
    
            return false;
        }
    };
    
    bool B1::operator ==(const A &a)
    {
        //Make sure that the passed type is same
        const B1* pB = dynamic_cast<const B1*>(&a);
        if(pB )
        {
            return compare(pB);
        }
    
        return false;
    }
    //Similarly implement for B2
    

    【讨论】:

    • 在基类中有一个非纯公共虚拟运算符 == 可能非常危险,因为对于忘记覆盖它的新派生类没有保护或警告,如果只是基础部分相等。
    • 是的,你是对的。编辑代码以使其成为纯虚拟
    • 虽然我同意 A 应该是抽象的,但我认为它根本不需要(或应该有)和 operator==。就目前而言,a == b 之类的表达式将根据ab 的顺序和类型而具有截然不同的行为,而它们可能期望是对称的。 operator== 仅对具有值语义的类型才真正有意义。
    【解决方案4】:

    如果您不关心类型 A 与类型 B 或 B 与 C 的比较等,那么您可以简单地为每个类实现一个重载的相等运算符:

    class A {
        public: int data;
    
        bool operator==(const A& rhs) {
            return (data == rhs.data);
        }
    };
    class B : public A {
        public: float more_data; bool something_else;
    
        bool operator==(const B& rhs) {
            return (A::operator==( data ) &&
                    more_data == rhs.more_data &&
                    something_else == rhs.something_else);
        }
    };
    

    但这很危险,因为如果你从 B 或 C 派生一个新的 D 类,你就会遇到问题。

    否则,您需要实现一些具有大量 dynamic_cast-ing 的比较器才能真正做到正确。或者,您可以实现一个函数来为每个对象创建一个哈希码并利用它,例如

    class A {
        public: int data;
    
        virtual long getHashCode() const {
            // compute something here for object type A
        }
    
        // virtual here just in case you need to overload it in B or C
        virtual bool equals( const A& obj ) const {
            return (typeid(*this) == typeid(obj) &&
                    getHashCode() == obj->getHashCode());
        }
    };
    
    class B : public A {
        public: float more_data; bool something_else;
    
        virtual long getHashCode() const {
            // compute something here for object type B
        }
    };
    
    class C : public A {
        public: double more_data;
    
        virtual long getHashCode() const {
            // compute something here for object type C
        }
    };
    

    如果您以某种方式将对象的类型合并到哈希码中(上面未显示),那么您也可以省去上面愚蠢的 typeid() 比较。

    【讨论】:

      【解决方案5】:

      如果您不介意引用子类的基类,那么双重调度:

      #include <iostream>
      
      class B;
      class C;
      
      class A
      {
      public:
          int data;
      
          virtual bool equals (const A* rhs) const
          {
              std::cout << " A==A ";
              return data == rhs->data;
          }
      
          virtual bool equals (const B* rhs) const {return false;}
          virtual bool equals (const C* rhs) const {return false;}
      };
      
      class B : public A
      {
      public:
          float some_data;
      
          virtual bool equals (const A* rhs) const
          {
              return rhs->equals (this);
          }
      
          virtual bool equals (const B* rhs) const
          {
              std::cout << " B==B ";
              return A::equals (static_cast<const A*> (rhs)) && some_data == rhs->some_data;
          }
      };
      
      class C : public A
      {
      public:
          double more_data;
      
          virtual bool equals (const A* rhs) const
          {
              return rhs->equals (this);
          }
      
          virtual bool equals (const C* rhs) const
          {
              std::cout << " C==C ";
              return A::equals (static_cast<const A*> (rhs)) && more_data == rhs->more_data;
          }
      };
      
      bool operator== (const A& lhs, const A& rhs)
      {
          return lhs.equals (&rhs);
      }
      
      int main (int argc, char* argv[])
      {
      
          A* one = new B;
          A* two = new B;
          A* three = new C;
      
          std::cout << (*one == *one) << std::endl;
          std::cout << (*one == *two) << std::endl;
          std::cout << (*one == *three) << std::endl;
          std::cout << (*three == *three) << std::endl;
      
          return 0;
      }
      

      不需要 dynamic_casts。

      【讨论】:

        猜你喜欢
        • 2012-10-04
        • 2012-09-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-09-24
        相关资源
        最近更新 更多