因此,在阅读了一些SO问题和答案之后,我仍然不明白为什么要使用
friend bool operator==( BaseClass const &left, BaseClass const &right )
而不是
bool operator==( BaseClass const &right )
现在我有这样的事情http://pastebin.com/pKsTabC0 (固定) - 它似乎工作正常。但也许我错过了什么?有什么建议吗?
更新1
好的,我更改了源代码,使其正常工作http://ideone.com/fIAmB。删除了不必要的虚拟和添加的const。我还是不明白为什么要用朋友......
答案 0 :(得分:10)
您的派生函数与父运算符的签名不同,因此它隐藏父对象而不是覆盖它。这意味着你无论如何也无法使用虚拟对象,因为左手参数的静态类型决定了被调用的函数。
这就是为什么虚拟比较的常规方法是一个非成员相等运算符,它分派给基类中的虚拟比较函数。
请考虑您对虚拟比较的特殊需求,因为它可能是一种设计气味,而是可以使用替代设计。
另请注意,成员比较(在本例中为相等)运算符通常应为const
。
编辑:看起来你可能只关心基于左手参数的静态类型的比较,这应该是一个更容易的问题。在这种情况下,您的代码处理所有情况,除非左手参数是一个类型,通过除继承(转换运算符或转换构造函数)之外的某种机制隐式转换为Base
或Derived
。如果您不关心这些情况,那么成员平等就没问题了。
最后要注意的是,如果比较完全可以通过公共界面进行,那么我们(几乎)总是喜欢非会员,非朋友的功能,无论它是否是运营商。
EDIT2(非会员,非朋友非常快速的概述):
例如,假设您的类具有公共key
方法,并且如果两个实例的键相等,则您希望将对象调用为相等。然后,不使用朋友或成员相等运算符,您可以编写单独的等式:
bool operator==(const MyType& left, const MyType& right)
{
return left.key() == right.key();
}
答案 1 :(得分:7)
使用成员函数本身没有任何问题,但是自由函数更通用。成员函数被强制使左侧操作数为BaseClass
类型或子类型,而自由函数接受任何可隐式转换为BaseClass
的类型,而不仅仅是属于{{1}}的类型继承树。
答案 2 :(得分:4)
我在virtual bool operator==( BaseClass const &right )
看到的最大问题是,如果没有多个动态调度*,以下断言将失败。
class Derived1 : public BaseClass {
bool operator==( BaseClass const &right ) override {
return true;
}
}
class Derived2 : public BaseClass {
bool operator==( BaseClass const &right ) override {
return false;
}
}
Derived1 d1;
Derived2 d2;
assert((d1 == d2) == (d2 == d1));
朋友的功能可以用来处理这个问题,但老实说,如果没有多次发送,这样做会很麻烦。即使有多个调度,这种多态等式运算符的正确语义也可能不容易出现。
*多重动态调度是对多个参数进行动态调度的能力,而不仅仅是在this
上。
答案 3 :(得分:2)
您要尝试制作两个独立的点。首先,将operator作为成员函数而不是独立函数来实现。其次,将其实现为虚拟功能(您甚至可以在标题中添加)。
“虚拟”部分你想说的是什么并不清楚。在您的代码示例中,派生类运算符不会覆盖基类运算符,因为它们的签名不匹配。你的例子中没有多态性。所以,我不明白你的意思是保持“工作正常”。它根本不起作用(作为“虚拟”操作符),因为在你的代码中实际上没有任何东西可以“工作”。目前,您的代码只是编译,但它没有做任何事情。提供试图证明多态行为的代码 - 然后我们将看它是否“正常”。
至于将二元运算符作为成员函数实现......这个问题已经不止一次被覆盖了。作为成员函数实现的二元运算符在隐式参数转换方面表现为“非对称”。这就是为什么将对称二元运算符实现为独立函数通常是一个更好的主意。