可能重复:
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?
有干净的方法吗?什么是我最好的选择?
谢谢!
答案 0 :(得分:14)
我记得读过公共 - 非虚拟/非公共 - 虚拟习语的简洁描述及其优点,但不是在哪里。 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 :(得分:2)
不同的派生类可以制作相同的对象吗?
如果是这样的话:double dispatch是一个选项:它确实需要在基类中重载,所以你将有依赖
如果不是:运算符==()中的解决方案检查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
}
这避免了所有派生类中的类型检查
答案 2 :(得分: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
答案 3 :(得分: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&lt;&gt; -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()比较。
答案 4 :(得分: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。