问题
Consider the following abstract class:
class Abstract {
public:
// ...
virtual bool operator==(const Abstract& rhs) const = 0;
// ...
};
Now suppose I'm creating multiple derived classes from this abstract class. However, each one uses a different algorithm when comparing with its own type, and a generic algorithm when comparing with any of the other derived classes. Between the following two options, which would be the better, more efficient option?
Option A:
class Derived : public Abstract {
public:
// ...
bool operator==(const Abstract& rhs) const {
// Code for comparing to any of the other derived classes
}
bool operator==(const Derived& rhs) const {
// Code for comparing to myself
}
// ...
};
Option B:
class Derived : public Abstract {
public:
// ...
bool operator==(const Abstract& rhs) const {
const Derived* tmp = dynamic_cast<const Derived*>(&rhs);
if (tmp) {
// Code for comparing to myself
}
else {
// Code for comparing to any of the other derived class
}
}
};
I'm really curious as to what advantages and disadvantages these options would have, as C++ typecasting is a relatively mysterious topic to me. Furthermore, which solution is more "standard", and does the second solution have any impacts on performance?
Is there possibly a third solution? Especially if there were many derived classes, each needing its own special comparison algorithm against different derived classes?
回答1:
I think that option B is what you are looking for if you're expecting the == operator to use the dynamic type of the argument. For example:
class base
{
public:
virtual bool operator ==( const base& other ) = 0;
};
class derived : public base
{
public:
bool operator ==( const base& other ) { return false; }
bool operator ==( const derived& other ) { return true; }
};
int main()
{
base* a = new derived;
base* b = new derived;
std::cout << ( *a == *b ) << std::endl;
}
This prints:
0
So operator ==( const base& other ) gets called, even if the actual dynamic type is derived.
回答2:
Your two methods are for different situation. For option A, the static type of rhs
is used to decide which function to call, and for option B the dynamic type is used.
So if you want your program to choose its behavior base on the "real" type of the argument, I think you should choose the second option. If types can be known at compile time, option A should be used since it gives better performance.
回答3:
You actually can do it third way using one of the techiniques to implement double dispatching. This approach is fully described in Item 31 of "More Effective C++". Here is small example:
#include <iostream>
class Derived1;
class Derived2;
class Base
{
public:
virtual bool operator==( Base& other) = 0;
virtual bool compare( Base& other) {return false;}
virtual bool compare( Derived1& other) {return false;}
virtual bool compare( Derived2& other) {return false;}
};
class Derived1 : public Base
{
public:
virtual bool operator==( Base& other) {return other.compare(*this);}
virtual bool compare( Base& other) {return false;}
virtual bool compare( Derived1& other) {return true;}
};
class Derived2 : public Base
{
public:
virtual bool operator==( Base& other) {return other.compare(*this);}
virtual bool compare( Base& other) {return false;}
virtual bool compare( Derived2& other) {return true;}
};
int main()
{
Base *a = new Derived1;
Base *b = new Derived1;
Base *c = new Derived2;
std::cout << (*a == *b) << std::endl;
std::cout << (*a == *c) << std::endl;
return 0;
}
Output:
1
0
回答4:
Unfortunately C++ have no multimethods that would choose the current function to call based on dynamic type information. You need double dispatch, visitor pattern or some other trick to implement the behavior.
来源:https://stackoverflow.com/questions/7452061/dynamic-casts-or-function-overloads