当比较两个对象(相同类型)时,有一个比较函数需要同一个类的另一个实例。如果我将它实现为基类中的虚函数,那么函数的签名也必须引用派生类中的基类。解决这个问题的优雅方法是什么?比较不应该是虚拟的吗?
class A
{
A();
~A();
virtual int Compare(A Other);
}
class B: A
{
B();
~B();
int Compare(A Other);
}
class C: A
{
C();
~C();
int Compare(A Other);
}
答案 0 :(得分:1)
我会像这样实现它:
class A{
int a;
public:
virtual int Compare(A *other);
};
class B : A{
int b;
public:
/*override*/ int Compare(A *other);
};
int A::Compare(A *other){
if(!other)
return 1; /* let's just say that non-null > null */
if(a > other->a)
return 1;
if(a < other->a)
return -1;
return 0;
}
int B::Compare(A *other){
int cmp = A::Compare(other);
if(cmp)
return cmp;
B *b_other = dynamic_cast<B*>(other);
if(!b_other)
throw "Must be a B object";
if(b > b_other->b)
return 1;
if(b < b_other->b)
return -1;
return 0;
}
这与.NET中的IComparable
模式非常相似,非常有效。
编辑:
对上述内容的一个警告是a.Compare(b)
(其中a
是A而b
是B)可能会返回相等,并且永远不会抛出一个例外,而b.Compare(a)
会。有时候这就是你想要的,有时却不是。如果不是,那么您可能不希望您的Compare
功能是虚拟的,或者您希望比较基础type_info
功能中的Compare
,如:
int A::Compare(A *other){
if(!other)
return 1; /* let's just say that non-null > null */
if(typeid(this) != typeid(other))
throw "Must be the same type";
if(a > other->a)
return 1;
if(a < other->a)
return -1;
return 0;
}
请注意,派生类'Compare
函数不需要更改,因为它们应该调用基类Compare
,其中type_info
比较将发生。但是,您可以使用dynamic_cast
替换被覆盖的Compare
函数中的static_cast
。
答案 1 :(得分:1)
也许,我会这样做:
class A
{
public:
virtual int Compare (const A& rhs) const
{
// do some comparisons
}
};
class B
{
public:
virtual int Compare (const A& rhs) const
{
try
{
B& b = dynamic_cast<A&>(rhs)
if (A::Compare(b) == /* equal */)
{
// do some comparisons
}
else
return /* not equal */;
}
catch (std::bad_cast&)
{
return /* non-equal */
}
}
};
答案 2 :(得分:1)
它取决于A,B和C的预期语义以及compare()的语义。比较是一个抽象的概念,不一定具有单一的正确含义(或任何含义,就此而言)。这个问题没有一个正确的答案。
这里有两个场景,其中比较意味着具有相同类层次结构的两个完全不同的事物:
class Object
{
virtual int compare(const Object& ) = 0;
float volume;
};
class Animal : Object
{
virtual int compare(const Object& );
float age;
};
class Zebra : Animal
{
int compare(const Object& );
};
我们可以考虑(至少)两种比较两种斑马的方法:哪种更老,哪种体积更大?两种比较都是有效且易于计算的;区别在于我们可以使用音量来比较斑马与任何其他物体,但我们只能使用年龄来比较斑马与其他动物。如果我们想要compare()来实现年龄比较语义,那么在Object类中定义compare()没有任何意义,因为语义不是在层次结构的这个级别定义的。值得注意的是,这些场景都不需要任何演员,因为语义是在基类级别定义的(比较音量时是对象,还是比较年龄时是动物)。
这引发了更重要的问题 - 某些类不适合单个catch-all compare()函数。通常,实现明确说明要比较的内容的多个函数更有意义,比如compare_age()和compare_volume()。这些函数的定义可以发生在语义变得相关的继承层次结构中,并且应该将它们调整为子类(如果需要适应它们)应该是微不足道的。使用compare()或operator ==()进行简单比较通常只对简单的类有意义,其中正确的语义实现是明显且明确的。
长话短说......“这取决于”。
答案 3 :(得分:0)
如果你的意思是B类或C类的Compare()应该总是传递给B类或C类的对象,无论签名是什么,你都可以使用指向实例而不是实例的指针,并尝试向下转换方法代码中的指针使用
之类的东西int B::Compare(A *ptr)
{
other = dynamic_cast <B*> (ptr);
if(other)
... // Ok, it was a pointer to B
}
(这样的重载只对于那些添加到其父级状态的派生类才有必要,这些类会影响比较。)
答案 4 :(得分:0)
比较必须是反思的,所以:
let a = new A
let b = new B (inherits from A)
if (a.equals(b))
then b.equals(a) must be true!
所以a.equals(b)
应该返回false,因为B可能包含A没有的字段,这意味着b.equals(a)
可能是假的。
因此,在C ++中,比较应该是虚拟的,我应该使用类型检查来查看参数与当前对象的“相同”类型。
答案 5 :(得分:0)
除了dynamic_cast之外,还需要传递引用或指针,可能是const。 Compare函数也可能是const。
class B: public A
{
B();
virtual ~B();
virtual int Compare(const A &Other) const;
};
int B::Compare(const A &Other) const
{
const B *other = dynamic_cast <const B*> (&Other);
if(other) {
// compare
}
else {
return 0;
}
}
编辑:必须在发布之前编译...
答案 6 :(得分:0)
我在C ++中几乎没有这个问题。与Java不同,我们不需要从同一根Object类继承所有类。在处理可比较的(/值语义)类时,它们不太可能来自多态层次结构。
如果在您的特定情况下需要是真实的,您将回到双重调度/多方法问题。有多种方法可以解决它(dynamic_cast,可能的交互功能表,访问者......)
答案 7 :(得分:0)
我建议不要把它变成虚拟的。唯一的缺点是,如果类不相同,您必须明确说明哪个比较使用。但是因为你必须这样做,你可能会发现一个错误(在编译时),否则会导致运行时错误......
class A
{
public:
A(){};
int Compare(A const & Other) {cout << "A::Compare()" << endl; return 0;};
};
class B: public A
{
public:
B(){};
int Compare(B const & Other) {cout << "B::Compare()" << endl; return 0;};
};
class C: public A
{
public:
C(){};
int Compare(C const & Other) {cout << "C::Compare()" << endl; return 0;};
};
int main(int argc, char* argv[])
{
A a1;
B b1, b2;
C c1;
a1.Compare(b1); // A::Compare()
b1.A::Compare(a1); // A::Compare()
b1.Compare(b2); // B::Compare()
c1.A::Compare(b1); // A::Compare()
return 0;
}