2009-11-19 48 views
10

可能重复:
What’s the right way to overload operator== for a class hierarchy?相等测试在C派生类++

在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? 

有没有干净的做法呢?我最好的选择是什么?

谢谢!

+2

可能的重复:http://stackoverflow.com/questions/1691007/whats-the-right-way-to-overload-operator-for-a-class-hierarchy –

+0

你想比较'T == T ',其中'T'可能是'A','B'或'C'(罚款),或者你想用'C'和'B'将'A'与'B'和'A'进行比较与'C'(可疑)? – sbi

+0

在上面的例子中,我想比较一与二和三​​。他们都是A指针。 – Imbue

回答

12

看完记得公共非虚拟/非公有制虚拟成语及其优点的简要描述,而不是在那里。 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; 
    } 
    } 
}; 
1

一个这样做的方式是使用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 
+0

在基类中拥有一个非纯公共虚拟运算符==可能非常危险,因为对于新的派生类没有任何保护或警告,它们会忘记覆盖它,并且如果只有基本部分相等,则最后所有比较相等。 –

+0

是的,你是对的。编辑代码以使其成为纯虚拟 – Naveen

+0

虽然我同意'A'应该是抽象的,但我不认为它需要(或应该有)和运算符==。就像“a == b”这样的表达式,根据“a”和“b”的顺序和类型,它们会有很大的不同,但它们可能会是对称的。 'operator =='对于具有值语义的类型才真正有意义。 –

2

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

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

如果没有:一个解决方案是运营商==()检查的typeid并返回false如果他们不同。否则,调用一个私有的equals()函数,派生类可以在其中执行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 
} 

这就避免了类型检查,在所有派生类

+0

这可以防止使用派生类B(比方说B2)与B进行比较(使用问题的层次结构)。 – 2009-11-19 17:56:20

+0

operator ==()仅在基类中定义,因此可以使用层次结构。 equal()函数必须是私有的(如前所述),只能由operator ==() – stefaanv

0

如果你不关心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的分配()的比较之上。

0

如果你不介意的基类指的是子类然后双击调度:

#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。