In a design of a class hierarchy, I'm using an abstract base class that declares various methods that the derived classes would implement. In a sense, the base class is as close to an interface as you can get in C++. However, there is an specific issue. Consider the code below which declares our interface class:
class Interface {
public:
virtual Interface method() = 0;
};
class Implementation : public Interface {
public:
virtual Implementation method() { /* ... */ }
};
Of course, this wouldn't compile, because you cannot return an abstract class in C++. To get around this problem I'm using the following solution:
template <class T>
class Interface {
public:
virtual T method() = 0;
};
class Implementation : public Interface<Implementation> {
public:
virtual Implementation method() { /* ... */ }
};
This solution works and is all fine and dandy, however, to me, it doesn't look very elegant, because of the redundant bit of text which would be the parameter for interface. I'd be happy if you guys could point our any other technical issues with this design, but that is my only concern at this point.
Is there any way to get rid of that redundant template parameter? Possibly using macros?
Note: The method in question has to return an instance. I'm aware that if method()
returned a pointer or a reference, there would be no issue.
No, a abstract type can't be returned directly , You should return a subclasses of abstract classes .
An abstract class is a class that is designed to be specifically used as a base class. An abstract class contains at least one pure virtual function. You declare a pure virtual function by using a pure specifier ( = 0 ) in the declaration of a virtual member function in the class declaration.
NO. Abstract methods(defintion) are overridden by base class' overriding methods.
Yes, it is reasonable and beneficial to mark explicitly as abstract a base class that should not be instantiated -- even in the absence of abstract methods. It enforces the common guideline to make non-leaf classes abstract. It prevents other programmers from creating instances of the class.
Interface::method()
cannot return an Interface
instance without using a pointer or reference. Returning a non-pointer, non-reference Interface
instance requires instantiating an instance of Interface
itself, which is illegal because Interface
is abstract. If you want the base class to return an object instance, you have to use one of the following:
A pointer:
class Interface
{
public:
virtual Interface* method() = 0;
};
class Implementation : public Interface
{
public:
virtual Interface* method() { /* ... */ }
};
A reference:
class Interface
{
public:
virtual Interface& method() = 0;
};
class Implementation : public Interface
{
public:
virtual Interface& method() { /* ... */ }
};
A template parameter:
template<type T>
class Interface
{
public:
virtual T method() = 0;
};
class Implementation : public Interface<Implementation>
{
public:
virtual Implementation method() { /* ... */ }
};
While you can't return by value for obvious reasons, it's entirely OK to return pointers or references -- this is called "covariant return type", and it is a valid form of virtual function overriding:
struct Base { virtual Base * foo(); }
struct Derived : Base { virtual Derived * foo(); }
The point is that Derived::foo()
is a genuine override, and not a base-hiding overload, because Derived*
is is a pointer to a derived class of Base
. The same applies to references.
In other words, if you have a Base * p
, and you call p->foo()
, you can always treat the result as a pointer to Base
(but if you have additional information, such as that your class is in fact Derived
, then you can use that information).
The opposite composition order, i.e. "contravariant argument types", is not allowed as part of C++.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With