2

I'm trying to make a copy of a derived class with only a base class pointer.

So if I have:

class BaseClass; //Abstract class with =0 functions
class LeftClass : BaseClass;
class RightClass : BaseClass;

And I have a function that takes a BaseClass as a parameter:

void Function(BaseClass* baseClass)

I want to make a copy of BaseClass, but I want to also copy the extended functionality of LeftClass OR RightClass, but I don't know which one was passed to the function - both are possible.

So I have something like this:

//global
vector<BaseClass*> myVector;

void Function(BaseClass* baseClass)
{
   BaseClass* baseClassCopy = new BaseClass(baseClass);
   myVector.push_back(baseClassCopy);
}   

And then I call the function with a left or right class

int main()
{
   LeftClass leftClass;
   Function(&leftClass);
   LeftClass* ResultOfCopy = myVector.at(0);
}

That code doesn't copy over the entire leftclass as a copy, is there a way to do this I'm overlooking?

Also BaseClass is abstract, some of the functions are =0 so I can't new one up. Otherwise there is a copy function in the other classes to use.

NathanOliver
  • 171,901
  • 28
  • 288
  • 402
Sam
  • 41
  • 2
  • 2
    Looks like you need the [clone functionality](https://stackoverflow.com/a/5148751/4342498) – NathanOliver Feb 27 '20 at 17:20
  • Is there a way to do it without modifying any of the classes? I'm unit testing, so I'm not supposed to change any code... – Sam Feb 27 '20 at 17:23
  • Oh wait I might have a solution from that. Thanks! :) – Sam Feb 27 '20 at 17:26
  • Wait, what if the BaseClass is abstract so I can't 'new' a new one? – Sam Feb 27 '20 at 17:28
  • @Sam Even if you could, it would `new` the wrong class. You fundamentally *need* to add a `clone` method (“virtual copy constructor”) to your class to obtain this functionality, short of cheating the way Nathan’s answer shows (but, really, don’t do that). – Konrad Rudolph Feb 27 '20 at 17:45

2 Answers2

2

If you can't change any of the classes to add a clone function then you can manually do that yourself by using dynamic_cast to determine its run time type and call the correct derived constructor. That would look like

void Function(BaseClass* baseClass)
{
    if (auto ptr = dynamic_cast<LeftClass*>(baseClass))
        myVector.push_back(new LeftClass(*ptr));
    if (auto ptr = dynamic_cast<RightClass*>(baseClass))
        myVector.push_back(new RightClass(*ptr)); 
}  

and you can see it working in this live example

NathanOliver
  • 171,901
  • 28
  • 288
  • 402
2

This kind of problem is usually solved by a clone() method.

class BaseClass
{
    virtual BaseClass* clone()            = 0
};

class LeftClass: public BaseClass
{
    virtual LeftClass* clone() override {return new LeftClass(*this);}
};
class RightClass: public BaseClass
{
    virtual RightClass* clone() override {return new RightClass(*this);}
};
class RightRightClass: public RightClass
{
    virtual RightRightClass* clone() override {return new RightRightClass(*this);}
};

Your function is now easy to write:

void Function(BaseClass* baseClass)
{
   BaseClass* baseClassCopy = !baseClass ? nullptr : baseClass->clone();
   myVector.push_back(baseClassCopy);
}   
Martin York
  • 257,169
  • 86
  • 333
  • 562
  • This will still create an object of type `BaseClass` though, which does not solve the issue of 'copying the functionality of LeftClass or RightClass' – Object object Feb 27 '20 at 18:44
  • @TheGoldKnight23 No. This will create an object of the correct type but return a pointer of `BaseClass` that points at the object of the correct type. Notice the calls to `new`: `return new LeftClass(*this);` and `return new RightClass(*this);` – Martin York Feb 27 '20 at 18:46
  • But you will not be able to access the functionality of `LeftClass` or `RightClass` through that pointer, although I see your point that _technically_ it does copy it and return the correct object (which is why `dynamic_cast` works) – Object object Feb 27 '20 at 18:48
  • @TheGoldKnight23 Yes you will!! The Note: `BaseClass` is a virtual base class. The functions you want to access are virtual methods that are overrideen in the derived class. So you have full access to these. – Martin York Feb 27 '20 at 18:50
  • Right sorry, though they meant access members not declared in `BaseClass`. Yes of course that works but why use `dynamic_cast` with run-time cost when you can use templates that are compile-time cost? – Object object Feb 27 '20 at 18:55
  • @TheGoldKnight23 OK. That is an interesting question. 1. I think `clone()` is usually the result of other bad design decisions that have backed you into a corner. 2. I think dynamic_cast is a bad solution as it is very brittle (but that is solved by using explicit virtual methods that removes the need for explicit manual casts). 3 Why pay the runtime cost? Never do that if you can help it. **BUT** you can't always determine at compile time the actual runtime type. In this case your method only works because the user took an automatic variable. – Martin York Feb 27 '20 at 20:16
  • What happened if the call was `Function(my_vector[0])` ie. make a copy of an object that already exists in the vector. You can't tell the runtime type as you only have a base class pointer. In this case your code would break as you make a copy of the base class not a copy of the underlying type. – Martin York Feb 27 '20 at 20:16
  • Damn I did not think that, (`Function(my_vector[0])` thats a bloody good point, Ill update my answer to reflect that. You are wrong about `auto` though, I could equally have used the template `BC*`, auto is compile time anyway and would likely be chosen as `BasicClass` by the compiler without the template – Object object Feb 27 '20 at 20:21
  • You can use [covariant return types](https://en.cppreference.com/w/cpp/language/virtual#Covariant_return_types) to return the corresponding pointers `virtual LeftClass* clone() override {return new LeftClass(*this);}` – L. F. Mar 07 '20 at 02:19
  • @L.F. Thanks for the link. I had suspected that was the case but did not have a reference to confirm it. – Martin York Mar 07 '20 at 02:42