Home > Back-end >  Copy Constructors of classes instantiating derived classes
Copy Constructors of classes instantiating derived classes

Time:02-17

I have unsuccessfully been trying to create a copy constructor of a class that instantiates a derived class.

Let's say I have the following pure virtual class:

class AbstractBar{
public:
    virtual void printMe() = 0;

};

Class Bar inherits from AbstractBar as follows:

class Bar: public AbstractBar {
    std::string name_;

public:
    explicit Bar(std::string name) : name_ {std::move(name)}{};

    void printMe() override { std::cout << name_ << std::endl; }
};

My class Foo now attempts to make use of polymorphism by declaring a pointer to type AbstractClass as follows:

class Foo{
    std::unique_ptr<AbstractBar> theBar_;

public:
    explicit Foo(std::unique_ptr<Bar> bar){
        theBar_ = std::move(bar);
    };

    void printBar(){
        theBar_->printMe();
    }
};

I do however want Foo to be copied so I add the following copy constructor:

    Foo(const Foo &other) {
        theBar_ = std::unique_ptr<AbstractBar>();
        *theBar_ = *(other.theBar_);
    }

And this is where it breaks.

What I gather is that this may be a problem since theBar in the copy constructor thinks it is pointing to an AbstractBar but when I try to copy the object it points to, in the next line, I actually give it a derived Bar class.

Is there a proper way to implement this copy constructor?

CodePudding user response:

First off, std::unique_ptr<T> is indeed unique. Therefore you cannot expect two things to point to the same instance-of-whatever by copying them. That said, I think what you're trying to do is clone whatever the "thing" is that is held by that member unique_ptr to allow a deep copy of a Foo.

If that is the case, you need a covariant clone. See below:

#include <iostream>
#include <string>
#include <memory>

struct AbstractBar
{
    virtual ~AbstractBar() = default;

    virtual std::unique_ptr<AbstractBar> clone() = 0;
    virtual void printMe() = 0;
};

class Bar : public AbstractBar
{
    std::string name_;

public:
    explicit Bar(std::string name) : name_{std::move(name)} {};

    std::unique_ptr<AbstractBar> clone() override
    {
        return std::make_unique<Bar>(name_);
    }

    void printMe() override
    {
        std::cout << name_ << std::endl;
    }
};

class Foo
{
    std::unique_ptr<AbstractBar> theBar_;

public:
    explicit Foo(std::unique_ptr<Bar> bar)
        : theBar_(std::move(bar))
    {
    }

    Foo(const Foo &other)
        : theBar_(other.theBar_->clone())
    {
    }

    void printBar()
    {
        theBar_->printMe();
    }
};

int main()
{
    Foo foo(std::make_unique<Bar>("Some String"));
    Foo bar(foo);

    foo.printBar();
    bar.printBar();
}

Important: foo and bar will each have their own Bar instance via a unique pointer to the abstract base of Bar, namely AbstractBar. Hopefully that was the intent. This isn't the only way to do this, but it is probably the easiest to understand.

  • Related