Reputation: 1199
So I want to override the pure abstract method in my derived classes but I got this error. Can someone help me see what happened and how can I complete it.
My Device
class;
class Device {
public:
Device();
Device(const Device& orig);
virtual ~Device();
virtual Device Clone() = 0;
}
And my derived class;
class Radar : public Device {
public:
Radar();
// Radar(const Radar& orig); // commenting so the compiler using its default copy constructor
virtual ~Radar();
Radar Clone();
};
Source file for my Radar
class;
Radar Radar::Clone() {
return *(new Radar(*this));
}
If I use Device
type in my Clone
method in Device
class, it will pop-up that Device
is an abstract class.
If I use void
type (which I'm assuming it's not what I want to have), it will show that I haven't implement this method.
What should I do?
Upvotes: 0
Views: 1171
Reputation: 30605
A classic approach to implementing this cloning technique is to use covariant return types for the Clone()
method. Coupled with modern RAII techniques (e.g. unique_ptr
et. al.) it offers a very flexible and safe combination to manage and clone the objects appropriately.
One of the advantages of the covariant return type is that you are able to obtain a clone an object (a deep copy) and the return type is at the same level in the hierarchy as the argument (i.e. the return is not always to the base class) and no immediate casting is required. In C++, pointers and references support covariance, values do not support covariance.
Using a smart pointer such as unique_ptr
is advised over raw painters to avoid memory leaks. The clone_unique
factory is modelled on the corresponding make_unique
utility from the standard library and returns a unique_ptr
. It contains explicit type checks on the class hierarchy of the argument and target types.
The solution does require use of std::unique_ptr
. If not available with your compiler, boost provides alternatives for these. There are a few other newer C++ language features, but these can be removed if required.
#include <type_traits>
#include <utility>
#include <memory>
class Device {
public:
virtual Device* Clone() const = 0;
};
class Radar : public Device {
public:
virtual Radar* Clone() const override {
// ^^^^^^ covariant return compared to Device::Clone
return new Radar(*this);
}
};
// Clone factory
template <typename Class, typename T>
std::unique_ptr<Class> clone_unique(T&& source)
{
static_assert(std::is_base_of<Class, typename std::decay<decltype(*source)>::type>::value,
"can only clone for pointers to the target type (or base thereof)");
return std::unique_ptr<Class>(source->Clone());
}
int main()
{
std::unique_ptr<Radar> radar(new Radar());
std::unique_ptr<Device> cloned = clone_unique<Device>(radar);
}
See this related answer for a longer example.
Upvotes: 1
Reputation: 106106
Your Clone
method will need to return pointers to the cloned objects... covariant return types only work that way (as returning by value is asking the caller to copy the returned value to the stack - that would be a memory leak when you've allocated it with new
).
So, it should be:
virtual Device* Clone() = 0;
...and later...
Radar* Clone(); // YES, it should be Radar* here - that uses C++'s support for
// "covariant return types", see also "UPDATE" discussion
Radar* Radar::Clone()
{
return new Radar(*this);
}
UPDATE - further explanation as requested
So, the idea with a clone function is that it can return a deep copy of whatever actual derived type your Device*
is currently addressing. Given that derived type might add data members that Device
lacked, it could be a larger object, and the caller has no ability to reserve the right amount of stack space in which to store it. For that reason, the object needs to be allocated dynamically with new
, and the predictably-sized Device*
is the caller's way to access the new object. It's legal for the clone function to return a Radar*
though - all that means is that client code that knows at compile time that it is dealing with a Radar
and clones it can continue to use it as a Radar
- accessing any extra members that Radar
provides.
Hope that helps clarify things. You might also want to do some background reading on Object Oriented programming.
Upvotes: 3
Reputation: 2983
Please try the following signature
virtual Device& Clone() = 0; or virtual Device* Clone() = 0;
//Body
Device& Radar::Clone() {
return Radar(*this));
}
Upvotes: -1