这是一个非常简单的类层次结构:
class A
{
public:
A( int _a ) : a( _a ) {}
virtual bool operator==( const A& right ) const
{
return a == right.a;
}
virtual bool operator!=( const A& right ) const
{
return !( *this == right );
}
int a;
};
class B : public A
{
public:
B( int _a, int _b ) : A( _a ), b( _b ) {}
virtual bool operator==( const B& right ) const
{
return A::operator==( right ) && b == right.b;
}
int b;
};
如您所见,operator!=在基类中定义。因为我非常懒,所以我不想在所有派生类中复制这么简单的代码。
Unfortunatley,使用此代码:
A a4(4), a5(5), a4bis(4);
assert( a4 == a4bis );
assert( a4 != a5 );
B b1(4,5), b2(4,6);
assert( !(b1 == b2) );
assert( b1 != b2 ); // fails because B::operator== is not called!
b1 != b2
返回false,因为它执行A::operator!=
,然后调用A::operator==
而不是B::operator==
(即使运算符是虚拟的,因为派生类版本参数不同,它们没有在vtable中链接。)
那么对于类层次结构来说,最好的地址是什么?=运算符?
一种解决方案是在每个班级重复一次,B
将有:
virtual bool operator!=( const B& right ) const
{
return !( *this == right );
}
但是当你有很多课程时,这很痛苦......我有30岁......
另一种解决方案是采用通用模板方法:
template <class T>
bool operator!=( const T& left, const T& right )
{
return !( left == right );
}
但是这会绕过任何类定义的任何!=
运算符....所以如果一个人以不同方式声明它(或者如果一个人声明==
本身调用!=
,则可能存在风险,它会以无限循环结束......)。所以我觉得这个解决方案非常不安全....除非我们可以限制模板用于从我们的层次结构的顶级类派生的所有类(在我的例子中为A
)....但是我认为这根本不可行。
注意:我还没有使用C ++ 11 ...抱歉。
答案 0 :(得分:5)
您在B
...
virtual bool operator==( const B& right ) const
... 不覆盖A
...
virtual bool operator==( const A& right ) const
...因为参数类型不同。 (只有协变返回类型才允许存在差异。)
如果您更正了这一点,那么您就可以选择如何将B
个对象与其他A
和A
个派生对象进行比较,例如:也许:
bool operator==( const A& right ) const override
{
if (A::operator==( right ))
if (typeid(*this) == typeid(right))
return b == static_cast<const B&>(right).b;
return false;
}
请注意,使用上面的typeid
比较意味着只有B
个对象才会比较相等:任何B
将与任何B
派生对象进行比较。这可能是也可能不是你想要的。
使用B::operator==
的实现,现有的!=
实现将正确包装operator==
。正如Jarod42所观察到的那样,您的A::operator==
并不健全,因为当左侧值为A
时,只有右侧对象的A
切片才会比较...你可能更喜欢:
virtual bool operator==(const A& right) const
{
return a == right.a && typeid(*this) == typeid(right);
}
这与上面的B::operator==
存在同样的问题:例如: A
将与不会引入其他数据成员的派生对象进行比较。
答案 1 :(得分:4)
这样的事情怎么样?
class A {
protected :
virtual bool equals(const A& right) const {
return (a == right.a);
}
public :
A(int _a) : a(_a) { }
bool operator==(const A& right) const {
return this->equals(right);
}
bool operator!=(const A& right) const {
return !(this->equals(right));
}
int a;
};
class B : public A {
protected :
virtual bool equals(const A& right) const {
if (const B* bp = dynamic_cast<const B*>(&right)) {
return A::equals(right) && (b == bp->b);
}
return false;
}
public :
B(int _a, int _b) : A(_a), b(_b) { }
int b;
};
将比较逻辑移动到单独的(虚拟)函数equals
,并从基类中定义的operator==
和operator!=
调用该函数。
不需要在派生类中重新定义运算符。要更改派生类中的比较,只需覆盖equals
。
请注意,上面代码中的dynamic_cast
用于确保运行时类型是执行比较的有效类型。 IE浏览器。对于B::equals
,它用于确保right
是B
- 这是必要的,因为否则right
将没有b
成员。