问题描述
从这个implementing operator== when using inheritance问题开始,我看到人们说operator==
不应设为virtual
(公认的答案甚至说“操作员不能是虚拟的” ,后来发现这是不正确的):
c ++允许操作员是虚拟的。但我不会将它们虚拟化。 多态和运算符实际上并不能很好地融合在一起(并且 自由的运算符功能显然根本不是虚拟的)。 ---- Johannes Schaub-小家伙
-1。使Person :: operator ==虚拟是不够的:必须重写Employee :: operator ==才能具有相同的签名。 而且,这仍然会导致道格拉斯指出的问题,即 比较操作的不对称,很奇怪。 ----卢·图拉(Luc Touraille)
我不明白为什么我不能做operator==
virtual
。我编写了这段代码,似乎可以很好地与多态性配合使用:
#include <iostream> #include <typeinfo> using namespace std; struct Base { int b1 = 1; int b2 = 2; virtual bool operator==(const Base& rhs) const { cout << "Base" << endl; if (typeid(*this) != typeid(rhs)) { cout << "false" << endl; return false; } else { cout << "true" << endl; return (b1 == rhs.b1) && (b2 == rhs.b2); } } }; struct Derived : public Base { int d1 = 3; int d2 = 4; virtual bool operator==(const Base& rhs) const override { cout << "Derived" << endl; if (typeid(*this) != typeid(rhs)) { cout << "false" << endl; return false; } else { cout << "true" << endl; const Derived& rrhs = static_cast<const Derived&>(rhs); return (b1 == rrhs.b1) && (b2 == rrhs.b2) && (d1 == rrhs.d1) && (d2 == rrhs.d2); } } }; int main() { Derived d; Base b1,b2; Base *bptr1,*bptr2,*bptr3; bptr1 = &b1; bptr2 = &d; bptr3 = &b2; if (*bptr2 == *bptr1) { cout << "1" << endl; } else { cout << "0" << endl; } if (*bptr3 == *bptr1) { cout << "1" << endl; } else { cout << "0" << endl; } if ((d == b1) != (b1 == d)) { cout << "xxx"; } return 0; }
@H_404_25@我的代码有问题吗?
解决方法
通过使用
typeid
和static_cast
可以解决对称性问题(a == b应该给出与b == a相同的结果)。是的,您的代码行为良好。我加入了行if (!(*bptr2 == *bptr1) && (*bptr1 == *bptr1)){ std::cout << "Oops"; } if (*bptr3 == *bptr1 && !(*bptr1 == *bptr3)){ std::cout << "Oops"; }
通过额外的测试。因此,您根本没有真正使用多态。您几乎消除了多态性的影响。
,这是您想要在程序中使用的语义的问题。
在您链接的问题中,OP具有
Person
和Employee
类。在他们的实现中,您可以使一个人Bob既有一个Person
对象(pBob
)也有一个Employee
对象(eBob
)表示,那么情况:
pBob == eBob -> true
和eBob == pBob -> false
这是您引用的警告所警告的不对称比较。我相信,这会让大多数人感到惊讶-因此是非常不受欢迎的代码。
如果您添加更多类:
Contractor
,Executive
,Salaried
,HourlyWorker
,这个问题也会变得很陌生。现在,如果您有两个Employee&
,则必须调查基础对象类型是什么,并且可能会跳过箍以正确的方式进行比较:Bob是一名承包商,但现在被录用了薪水,怎么办您将contractorBob
和salariedBob
对象与Employee
比较运算符进行比较,以发现当您所有的人都是虚拟比较运算符时,它们实际上是同一位员工/个人吗?另一方面,可能是您在一个不对称比较运算符很明显的领域工作。
最后,它取决于您希望类的语义是什么,以及如何最好地实现对您有意义的语义。
,无法定义在多态类层次结构中工作的通用比较运算符。这是几个例子。
示例一。我们正在建立算术层次结构。在
BigNumber
上操作,基础框架将选择正确的表示形式。class BigNumber { ... }; class BigInteger : public Number { ... }; class BigRational : public Number { ... }; class BigFloating : public Number { ... };
如果我们遵循这样的哲学,即不同动态类型的两个对象永远不相等,则
BigInteger(0)
不等于BigRational(0,1)
或BigFloating(0.0)
。这破坏了我们所知道的算法。示例二。我们正在建立几何层次结构。
class Shape { ... }; class Triangle : public Shape { ... }; class Rectangle : public Shape { ... };
到目前为止一切顺利。
Triangle
永远不会等于Rectangle
,对吧?让我们添加颜色。class ColouredTriangle : public Triangle { ... };
糟糕!现在,我们的小型CAD程序已停止工作,因为我们用
Triangle
替换了ColouredTriangle
中的一个Triangle
,即使几何引擎对颜色一无所知,只对几何属性感兴趣。更糟糕的是,即使我们将所有ColouredTriangle
替换为{{1}},并且如果我们希望几何引擎完全像以前一样工作,所有颜色也必须相同。也许在某些应用程序域中,首先比较类型ID是一种比较对象的可靠方法,但是我还没有看到。
这与您的编程语言无关。