C++ 什么';对于类层次结构,重载运算符==的正确方法是什么?
假设我有以下类层次结构:C++ 什么';对于类层次结构,重载运算符==的正确方法是什么?,c++,operator-overloading,C++,Operator Overloading,假设我有以下类层次结构: class A { int foo; virtual ~A() = 0; }; A::~A() {} class B : public A { int bar; }; class C : public A { int baz; }; 对于这些类,重载操作符==的正确方法是什么?如果我让它们都是免费函数,那么B和C就不能不强制转换就利用A的版本。它还可以防止某些人只引用a进行深入比较。如果我将它们设置为虚拟成员函数,则派生版本可能如
class A
{
int foo;
virtual ~A() = 0;
};
A::~A() {}
class B : public A
{
int bar;
};
class C : public A
{
int baz;
};
对于这些类,重载操作符==
的正确方法是什么?如果我让它们都是免费函数,那么B和C就不能不强制转换就利用A的版本。它还可以防止某些人只引用a进行深入比较。如果我将它们设置为虚拟成员函数,则派生版本可能如下所示:
bool B::operator==(const A& rhs) const
{
const B* ptr = dynamic_cast<const B*>(&rhs);
if (ptr != 0) {
return (bar == ptr->bar) && (A::operator==(*this, rhs));
}
else {
return false;
}
}
bool B::operator==(常量A和rhs)常量
{
常数B*ptr=动态施法(&rhs);
如果(ptr!=0){
返回(bar==ptr->bar)&&(A::operator==(*this,rhs));
}
否则{
返回false;
}
}
再说一次,我还是要投(感觉不对)。有没有更好的方法
更新:
到目前为止,只有两个答案,但看起来正确的方法类似于赋值运算符:
- 使非叶类抽象化
- 非叶类中受保护的非虚拟
- 叶类中的公共非虚拟
任何试图比较两个不同类型对象的用户都不会编译,因为基函数受保护,叶类可以利用父版本来比较数据的一部分。 < P>对于这种层次结构,我肯定会遵循Scott Meyer的有效C++建议,避免使用任何具体的基类。无论如何,你似乎都在这样做 我将把
operator==
实现为一个免费函数,可能是friends,仅用于具体的叶节点类类型
如果基类必须有数据成员,那么我将在基类(isEqual
)中提供一个(可能是受保护的)非虚拟助手函数,派生类的操作符==
可以使用该函数
例如
通过避免使用在抽象基类上工作的运算符==
,并保护比较函数,您永远不会在客户端代码中意外地出现回退,在客户端代码中,只比较两个不同类型对象的基部分
我不确定我是否会实现一个带有动态转换的虚拟比较函数,我不愿意这样做,但如果确实需要它,我可能会在基类中使用一个纯虚拟函数(不是操作符==
),然后在具体的派生类中重写它,就像这样,对派生类使用运算符==
bool B::pubIsEqual( const A& rhs ) const
{
const B* b = dynamic_cast< const B* >( &rhs );
return b != NULL && *this == *b;
}
bool B::publisequal(const A&rhs)const
{
常数B*B=动态施法<常数B*>(&rhs);
返回b!=NULL&&*this===b;
}
前几天我遇到了同样的问题,我想出了以下解决方案:
struct A
{
int foo;
A(int prop) : foo(prop) {}
virtual ~A() {}
virtual bool operator==(const A& other) const
{
if (typeid(*this) != typeid(other))
return false;
return foo == other.foo;
}
};
struct B : A
{
int bar;
B(int prop) : A(1), bar(prop) {}
bool operator==(const A& other) const
{
if (!A::operator==(other))
return false;
return bar == static_cast<const B&>(other).bar;
}
};
struct C : A
{
int baz;
C(int prop) : A(1), baz(prop) {}
bool operator==(const A& other) const
{
if (!A::operator==(other))
return false;
return baz == static_cast<const C&>(other).baz;
}
};
结构A
{
int foo;
A(int-prop):foo(prop){}
虚拟~A(){}
虚拟布尔运算符==(常量A和其他)常量
{
如果(类型ID(*此)!=类型ID(其他))
返回false;
返回foo==other.foo;
}
};
结构B:A
{
int-bar;
B(int-prop):A(1),bar(prop){}
布尔运算符==(常数A和其他)常数
{
如果(!A::operator==(其他))
返回false;
返回条==静态_转换(其他).bar;
}
};
结构C:A
{
int baz;
C(int-prop):A(1),baz(prop){}
布尔运算符==(常数A和其他)常数
{
如果(!A::operator==(其他))
返回false;
return baz==static_cast(其他).baz;
}
};
我不喜欢的是typeid检查。您对此有何看法?如果您合理地假设两个对象的类型必须相同才能相等,那么有一种方法可以减少每个派生类中所需的锅炉板数量。这样做是为了保护虚拟方法并将其隐藏在公共接口后面。用于实现
equals
方法中的样板代码,因此派生类不需要这样做
class A
{
public:
bool operator==(const A& a) const
{
return equals(a);
}
protected:
virtual bool equals(const A& a) const = 0;
};
template<class T>
class A_ : public A
{
protected:
virtual bool equals(const A& a) const
{
const T* other = dynamic_cast<const T*>(&a);
return other != nullptr && static_cast<const T&>(*this) == *other;
}
private:
bool operator==(const A_& a) const // force derived classes to implement their own operator==
{
return false;
}
};
class B : public A_<B>
{
public:
B(int i) : id(i) {}
bool operator==(const B& other) const
{
return id == other.id;
}
private:
int id;
};
class C : public A_<C>
{
public:
C(int i) : identity(i) {}
bool operator==(const C& other) const
{
return identity == other.identity;
}
private:
int identity;
};
A类
{
公众:
布尔运算符==(常数A&A)常数
{
回报等于(a);
}
受保护的:
虚拟布尔等于(常数A&A)常数=0;
};
模板
A类:公共A
{
受保护的:
虚拟布尔等于(常数A&A)常数
{
const T*other=动态施法(&a);
返回其他!=nullptr&&static_cast(*this)==*other;
}
私人:
bool操作符==(const A_&A)const//强制派生类实现它们自己的操作符==
{
返回false;
}
};
B类:公共A_
{
公众:
B(inti):id(i){}
布尔运算符==(常量B和其他)常量
{
返回id==other.id;
}
私人:
int-id;
};
C类:公共A类_
{
公众:
C(inti):恒等式(i){}
布尔运算符==(常量C和其他)常量
{
返回标识==other.identity;
}
私人:
智力同一性;
};
在上查看演示
void foo(const MyClass& lhs, const MyClass& rhs) {
if (lhs == rhs) {
MyClass tmp = rhs;
// is tmp == rhs true?
}
}
如果你不想使用Cube,并且确保你不会意外地比较B的实例和C的实例,那么你需要用Scott Meyers在更有效的C++ 33中提出的方式来重构类层次结构。实际上,该项处理赋值运算符,如果用于非相关类型,则它实际上毫无意义。在比较操作的情况下,在比较B和C的实例时返回false是有意义的
下面是使用RTTI的示例代码,它并没有将类层次结构划分为concreate leaf和abstract base 这个示例代码的好处是,在比较不相关的实例(如B和C)时,不会得到std::bad_cast。尽管如此,编译器仍将void foo(const MyClass& lhs, const MyClass& rhs) {
if (lhs == rhs) {
MyClass tmp = rhs;
// is tmp == rhs true?
}
}
#include <iostream>
#include <string>
#include <typeinfo>
#include <vector>
#include <cassert>
class A {
int val1;
public:
A(int v) : val1(v) {}
protected:
friend bool operator==(const A&, const A&);
virtual bool isEqual(const A& obj) const { return obj.val1 == val1; }
};
bool operator==(const A& lhs, const A& rhs) {
return typeid(lhs) == typeid(rhs) // Allow compare only instances of the same dynamic type
&& lhs.isEqual(rhs); // If types are the same then do the comparision.
}
class B : public A {
int val2;
public:
B(int v) : A(v), val2(v) {}
B(int v, int v2) : A(v2), val2(v) {}
protected:
virtual bool isEqual(const A& obj) const override {
auto v = dynamic_cast<const B&>(obj); // will never throw as isEqual is called only when
// (typeid(lhs) == typeid(rhs)) is true.
return A::isEqual(v) && v.val2 == val2;
}
};
class C : public A {
int val3;
public:
C(int v) : A(v), val3(v) {}
protected:
virtual bool isEqual(const A& obj) const override {
auto v = dynamic_cast<const C&>(obj);
return A::isEqual(v) && v.val3 == val3;
}
};
int main()
{
// Some examples for equality testing
A* p1 = new B(10);
A* p2 = new B(10);
assert(*p1 == *p2);
A* p3 = new B(10, 11);
assert(!(*p1 == *p3));
A* p4 = new B(11);
assert(!(*p1 == *p4));
A* p5 = new C(11);
assert(!(*p4 == *p5));
}