What is the proper way to overload operators in abstract base classes?
Suppose I have an abstract base cl开发者_如何学Goass, that just defines a container on which addition can be performed:
class Base {
public:
virtual ~Base() {}
virtual Base operator+(const Base& rhs) =0;
};
Then I want subclasses of Base to provide the actual operation:
class Derived: public Base {
public:
Base operator+(const Base& rhs) { // won't compile
// actual implementation
}
};
Here is my problem: operator+() is supposed to return a new Base object, but Base being abstract it won't compile.
I tried to get around that by using a factory to return a reference to a Base object, but then in the body of the operator I find myself doing casts, because the addition only makes sense on Derived objects.
In any case, it feels like I am biting my own tail, is there a proper solution to this?
UPDATE: Based on the answers so far, it seems I am using the wrong pattern. I want to separate the interface from the implementation, so that library code only has to know the interface and client code provides the implementation. I tried to do that by providing the interface as an abstract base class, and the implementation as subclasses.
UPDATE2: My question was actually 2 questions, a concrete one (about overloading operators in abstract classes) and another about my intent (how do I allow the client to customize the implementation). The former has been answered: don't. For the latter, it seems that the Interface Class pattern I use is actually a good one to solve that problem (according to Griffiths and Radford), it's just that I should not mess with overloaded operators.
The best thing is not to.
operator+
returns a value and you can't return a value of an abstract type, by definition. Overload the operators only for concrete types and avoid inheriting from concrete types to prevent "slicing by overloaded operator".
Overload symmetric binary operators like operator+
as free functions and you can control which combinations of types can be sensibly combined, and conversely prevent the combination of objects of types for which the combination doesn't make sense.
If you have a valid way of performing an "add" via two base class references and creating a new object you will have to return via a pointer, reference or pointer-wrapping smart object. Because you can't preserve the conventional semantics of +
I would recommend using a named function, e.g. Add()
instead of making an operator+
with a "surprising" syntax.
The usual solution to this is Jim Coplein's Algebraic Hierarchy pattern. See the following:
Coplein's original paper (particularly Algebraic Hierarchy)
Wikibooks
To elaborate a bit, you essentially need a concrete wrapper class that holds a polymorphic pointer to the actual derived class object. Define the operators in question to forward to the underlying representation while preserving the value semantics (rather than object semantics) you're looking for. Make sure that wrapper class uses the RAII idiom so you don't leak memory with every temporary object.
template<class C>
class Base {
public:
virtual ~Base() {}
virtual C operator+(const C& rhs) =0;
};
class Derived: public Base<Derived> {
public:
might work (barring imcomplete class problem).
Base
being abstract, you cannot instantiate it, so returning by value is out of the question. That means you'll have to return by pointer or reference. Returning by reference is dangerous since operator+
is likely to return a temporary value.
That leaves by pointer, but that would be very weird. And there's the whole question of how to release the pointer when you no longer need it. Using a smart pointer might be a solution to that problem, but you still have the whole "operator+
returns a pointer" issue.
So, what are you trying to achieve? What does Base
represent? Does it make sense to add two instance of a Base
subclasses?
I want to separate the interface from the implementation, so that library code only has to know the interface and client code provides the implementation. I tried to do that by providing the interface as an abstract base class, and the implementation as subclasses.
Use the pimpl idiom:
struct Base {
virtual ~Base() = 0;
virtual std::auto_ptr<Base> clone() = 0;
virtual void add(Base const &x) = 0; // implements +=
};
struct UserInterface {
UserInterface() : _pimpl(SomeFactory()) {}
UserInterface(UserInterface const &x) : _pimpl(x._pimpl->clone()) {}
UserInterface& operator=(UserInterface x) {
swap(*this, x);
return *this;
}
UserInterface& operator+=(UserInterface const &x) {
_pimpl->add(*x._pimpl);
}
friend void swap(UserInterface &a, UserInterface &b) {
using std::swap;
swap(a._pimpl, b._pimpl);
}
private:
std::auto_ptr<Base> _pimpl;
};
UserInterface operator+(UserInterface a, UserInterface const &b) {
a += b;
return a;
}
To actually implement Base::add, you'll need either 1) double dispatch and dealing with the resulting overload explosion, 2) require that only one derived class of base is used per program execution (still using pimpl as a compilation firewall, e.g. for swapping out shared libraries), or 3) require derived classes know how to deal with a generic base or will throw an exception.
use the dynamic type and base type. in the base class to enumerate derived class is impossible since the operator overloading must be static at all time. The most convenience option would be to use 'dynamic' type in the overloading arguments. Following is an example.
public class ModelBase
{
public abstract static string operator +(ModelBase obj1, dynamic obj2)
{
string strValue = "";
dynamic obj = obj1;
strValue = obj.CustomerID + " : " + obj2.CustomerName;
return strValue;
}
}
精彩评论