我已经实现了两个类(父类和派生类)比较运算符,但测试它我注意到在使用指针的情况下会出现令人困惑的行为。此外,我还有一些关于良好实践的问题。
这是代码:
struct A
{
int getN() { return _n; }
virtual bool equals(A &other) {
return getN() == other.getN();
}
friend bool operator==(const A &one, const A &other);
A(int n) : _n(n) { }
private:
int _n;
};
bool operator==(const A &one, const A &other) {
return one._n == other._n;
}
struct B : public A
{
friend bool operator==(const B &one, const B &other);
B(int n, int m = 0) : A(n), _m(m) { }
private:
int _m;
};
bool operator==(const B &one, const B &other) {
if( operator==(static_cast<const A&>(one), static_cast<const A&>(other)) ){
return one._m == other._m;
} else {
return false;
}
}
int main()
{
A a(10), a2(10);
B b(10, 20), b2(10, 20), b3(10, 30);
A *a3 = new B(10, 20);
bool x1 = a == a2; // calls A operator (1)
bool x2 = b == b2; // calls B operator (2)
bool x3 = a == b; // calls A operator (3)
bool x4 = b == a; // calls A operator (4)
bool x5 = b == *a3; // calls A operator (5)
bool x6 = a == b3; // calls A operator (6)
bool x7 = b3 == a; // calls A operator (7)
return 0;
}
将A实例与B实例进行比较,调用A类操作符,这是正确的行为吗?
第5点是让我感到困惑的第5点。 a3
声明为A,但实例为B,但调用了A类运算符。有什么方法可以解决这个问题吗?
如果操作符是作为实例方法实现的,则根据它使用A对象或B对象调用,执行的方法是不同的。例如:
a == b // executes A::operator==
b == a // executes B::operator==
我认为这是令人困惑且容易出错的,必须避免。我是对的吗?
答案 0 :(得分:2)
将
A
个实例与B
个实例进行比较,调用A
类运算符,这是正确的行为吗?
是的,因为这是唯一适用的超载。
如果运算符是作为实例方法实现的,则根据它使用
A
对象或B
对象调用,执行的方法是不同的。 [...]我认为这是令人困惑和容易出错的,必须避免。
是的,这不是一个好主意,因为相等运算符必须是对称的。尽管可以通过两个独立的运算符对称地实现它,但它在代码中引入了维护责任。
解决此问题的一种方法是扩展您的equals
成员函数,并让每个子类为其自己的类型实现相等:
struct A {
int getN() const { return _n; }
virtual bool equals(A &other) const {
return getN() == other.getN();
}
friend bool operator==(const A &one, const A &other);
A(int n) : _n(n) { }
private:
int _n;
};
struct B : public A
{
B(int n, int m = 0) : A(n), _m(m) { }
virtual bool equals(B &other) const {
return A::equals(*this, other) && _m == other._m;
}
private:
int _m;
};
bool operator==(const A &one, const A &other) {
return typeid(one)==typeid(two) && one.equals(two);
}
&#34;心脏&#34;此实现的结果是typeid
operator,它允许您在运行时检查类型是否相等。只有当one.equals(two)
和one
的类型完全相同时,才会对two
进行虚拟调用。
这种方法将责任与阶级本身进行平等比较。换句话说,每个类都需要知道如何比较它自己的实例,并且可选地依赖它的基础来比较它的状态。
答案 1 :(得分:0)
将
A
个实例与B
个实例进行比较,调用A
类运算符,是 这是正确的行为吗?
B&
可以转换为const A&
,但A&
无法转换为const B&
。因此,比较a == b
实际上只有一个选择。
“班级经营者”不是一件事。在您的代码中,您已将operator==
实施为非会员功能,您还成为A
和B
的朋友。
第5点是让我感到困惑的第5点。
a3
被声明为A
但实例为B
,但调用了类A
运算符。有没有 解决这个问题的方法?
取消引用a3
会返回A&
,而非B&
。
如果运算符是作为实例方法实现的,则取决于它 使用
A
对象或B
对象调用,执行的方法是 不同。例如:a == b // executes A::operator== b == a // executes B::operator==
我认为这是令人困惑且容易出错的,必须避免。上午 我没错?
如果将operator==
实现为实例方法,则在左侧操作数上调用它。在第一种情况下,它是a
,在第二种情况下是b
。因此b == a
仅在您实施bool B::operator==(const A&)
时才有效。
答案 2 :(得分:0)
将A实例与B实例进行比较,调用A类操作符,这是正确的行为吗?
由于A
中没有任何虚函数,因此只处理function overloading
,这意味着编译器决定在编译时调用哪个函数。由B
公开继承自A
,B
的指针或引用可以隐式转换为A
,但反之亦然。在这种情况下,它意味着“除非两个参数都是B
,否则只会调用第一个分辨率”。请注意,当您取消引用指针时,只有指针类型在编译时确定类型很重要,因此如果A *pointer
指向A
或B
的实例,则在这种情况下无关紧要,{ {1}}始终具有*pointer
类型。
如果您希望根据实际类型调用函数,则需要使用虚函数,可在此处找到A
如何执行此操作的详细信息implementing operator== when using inheritance