我在virtual operator==
课程中定义了Base
。但由于某种原因,它似乎并没有真正被视为虚拟。
请参阅此示例代码:
#include <iostream>
#include <boost/unordered_set.hpp>
template<class T> struct EqualByValue {
bool operator()(T const* a, T const* b) const { return *a == *b; }
};
struct Base {
virtual bool operator==(Base const& other) const {
std::cerr << "Base==" << std::endl;
}
};
struct Derived : Base {
virtual bool operator==(Derived const& other) const {
std::cerr << "Derived==" << std::endl;
}
};
int main(int argc, char** argv){
typedef boost::unordered_set<Base*, boost::hash<Base*>, EqualByValue<Base> >
MySet;
MySet s;
Derived* d1 = new Derived();
Derived* d2 = new Derived();
s.insert(d1);
s.insert(d2);
s.find(d2);
delete d1; delete d2; return 0;
}
输出为Base==
,而不是所需的输出Derived==
。
为什么会这样,我该如何解决?
答案 0 :(得分:6)
问题是你实际上没有覆盖operator==
,因为原来的签名不同。让我们通过使用一个名为override
的有用的C ++ 11特性来证明:
struct Derived : Base {
virtual bool operator==(Derived const& other) const override {
// ^^^^^^^^
std::cerr << "Derived==" << std::endl;
}
};
Compiling it with GCC会导致以下错误:
main.cpp:15:8: error: ‘bool Derived::operator==(const Derived&) const’ marked override, but does not override
bool operator==(Derived const& other) const override {
要解决此问题,只需将Derive
的{{1}}修改为与operator==
具有相同的签名:
Base
尽可能使用struct Derived : Base {
bool operator==(Base const& other) const override {
std::cerr << "Derived==" << std::endl;
// ...
}
};
,这样您就可以让编译器检测到这些错误。
答案 1 :(得分:2)
( 我在最后添加了一个更简单的解决方案,如果两个对象的动态类型不同,则可以假设相等性应该为假。 )
Derived中的operator==
不会覆盖Base中的operator==
。有不同的签名,一个接受Base&amp;作为other
,另一个是Derived&amp ;.它们必须相同才能覆盖工作(您可以稍微更改返回类型,但不能更改参数类型。)
一种解决方案是使用double dispatch解决此问题。您定义了一个名为real_comparator
的方法(或者可能是一个自由函数),它将实际进行比较工作。 real_comparator
有四个版本,每个版本都有两个版本。
当我们a==b
时,我们希望将两个变量的动态类型考虑在内。编译器将a==b
重写为a.operator==(b)
,因此,默认情况下,只有a
参与多态。我们想要改变这一点,以便激活两个变量(以及所有四种可能性)。
诀窍在return other.real_comparator(*this);
struct Derived;
struct Base {
virtual bool real_comparator(Base const& /*other*/) const {
std::cerr << "Base == Base?" << std::endl;
return false;
}
virtual bool real_comparator(Derived const& /*other*/) const {
std::cerr << "Base == Derived?" << std::endl;
return false;
}
virtual bool operator==(Base const& other) const {
return other.real_comparator(*this);
}
};
struct Derived : Base {
virtual bool real_comparator(Base const& /*other*/) const override {
std::cerr << "Derived == Base?" << std::endl;
return false;
}
virtual bool real_comparator(Derived const& /*other*/) const override {
std::cerr << "Derived == Derived?" << std::endl;
return false;
}
virtual bool operator==(Base const& other) const override {
return other.real_comparator(*this);
}
};
我认为这段代码可以简化一些,特别是如果你有一条规则说“如果两个对象的动态类型不同,则比较的结果总是假的”,或类似的东西。
可以使用更简单的解决方案,但这取决于您要解决的问题。假设,如果两个对象具有不同的动态类型,则比较应该返回false:
#include<typeinfo> // We need this for typeid to work
using namespace std;
struct Base {
virtual bool operator==(Base const& other) const {
if(typeid(other) != typeid(*this))
return false;
else
cout << "A pair of Bases" << endl;
return true; // replace this with code to compare two Base objects
}
};
struct Derived : Base {
virtual bool operator==(Base const& other) const override {
if(typeid(other) != typeid(*this))
return false;
else
cout << "A pair of Deriveds" << endl;
// So 'other' is actually a Derived.
const Derived * derived_pointer = dynamic_cast<const Derived*>(&other);
// Now, we can compare 'this' to 'derived_pointer', both pointers to Derived
return derived_pointer == this; // replace this with code to compare two Derived
}
};
这应该是正确的,但也许可以在某些方面进行改进。有什么反馈意见吗?