C++ 你应该去哪里运算符是否可以在类层次结构中定义?
下面是一个非常简单的类层次结构:C++ 你应该去哪里运算符是否可以在类层次结构中定义?,c++,operator-overloading,comparison-operators,C++,Operator Overloading,Comparison Operators,下面是一个非常简单的类层次结构: 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 =
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;
};
如您所见,接线员!=是在基类中定义的。因为我很懒,所以我不想在所有派生类中重复这样一个简单的代码
不幸的是,使用此代码:
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 );
}
模板
接线员=(常数T和左、常数T和右)
{
返回!(左==右);
}
但这绕过了任何=由任何类定义的code>运算符…因此,如果以不同的方式声明它(或者如果声明一个=
本身调用!=
,它将以无限循环结束…)可能会有风险。所以我觉得这个解决方案非常不安全……除非我们可以限制模板用于从我们层次结构的顶级类派生的所有类(A
,在我的示例中)……但我认为这根本不可行
注意:我还没有使用C++11…很抱歉。您在B
中的函数
virtual bool operator==( const B& right ) const
virtual bool operator==( const A& right ) const
…不会覆盖A
中的功能
virtual bool operator==( const B& right ) const
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;
}
这与上面的B::operator==
存在相同的问题:例如A
会将unequal与没有引入更多数据成员的派生对象进行比较。类似的情况如何
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;
};
A类{
受保护的:
虚拟布尔等于(常数A和右)常数{
返回值(a==右.a);
}
公众:
A(int_A):A(_A){}
布尔运算符==(常量A和右侧)常量{
返回此->等于(右);
}
布尔运算符!=(常量A和右侧)常量{
返回!(此->等于(右));
}
INTA;
};
B类:公共A{
受保护的:
虚拟布尔等于(常数A和右)常数{
if(常量B*bp=动态_投射(&R)){
返回A::等于(右)&&(b==bp->b);
}
返回false;
}
公众:
B(int_a,int_B):a(_a),B(_B){
int b;
};
将比较逻辑移动到一个单独的(虚拟)函数equals
,并从操作符==
和操作符调用该函数=代码>在基类中定义
这些运算符不需要在派生类中重新定义。要更改派生类中的比较,只需重写equals
请注意,上面代码中的dynamic_cast
用于确保运行时类型是执行比较的有效类型。例如,对于B::equals
,它用于确保right
是B
-这是必要的,因为否则right
将不会有B
成员。此外,目前,a(42)==B(42,0)
当您仅比较A
部分时……为了清晰起见,并确保A继续独立工作(如果您不希望,您不需要从中派生B),请执行!=对于A,B,C,D和你们等级体系中的任何东西。同样,如果你不需要它们,为什么你需要派生呢?有了它们,你就有了B(42,0)!=A(42)
但仍然A(42)==B(42,0)
。这需要多次调度。你能再多说几句你的方法吗?谢谢:-)@Jarod42:这是你的选择(我从OP中复制了这个行为)。如果您希望相等运算符是可交换的,那么双重分派(如您所建议的)确实会有所帮助。谢谢!每个类都可以覆盖第三个等于
的函数,这是解决我问题的好方法……根据双重分派:我对答案有一种不好的感觉,但没有任何警告——问题是在层次结构中不同(!=
)。这些动态转换和类型ID真的安全吗(即使在使用模板类时,因为我的类Hirrachy有模板…)@jpo38:它们是安全的,是的…每个模板实例化都会创建不同的RTTI/typeinfo对象。谢谢。您的代码运行良好,但我更喜欢Sander De Dycker
解决方案,因为我更喜欢派生类(B)将a对象强制转换为B,而不是按照您的建议使用父类(a)进行强制转换(如果类层次结构庞大,可能会导致代码难以阅读)。@jpo38:我没有查看他的代码进行比较,而是根据您提到的具体标准—“而不是使用父类(a)“按你的建议做演员”-我根本没有提议…上面唯一的强制转换是在B::operator=
中,它将其他B
对象强制转换到B&
以访问它们的B
成员。也许你把该代码误认为是A::operator=
。覆盖显示它是用于B
。好的,你完全正确,所以谢谢。反正我都投了你们两个的票;-)。谢谢