Deep copy constructor with std::vector of smart po

2020-03-05 06:50发布

Let's say I have a class FooContainer that aggregates unique_ptr objects of type Foo

#include <vector>
#include <memory>

class FooContainer
{
protected:
std::vector<std::unique_ptr<Foo>> many;
//other attributes
public:
FooCoontainer(const FooContainer&);
//handling functions for Foo
};

The question is how to correctly implement deep copy constructor, and what is syntax for it. Simply assigning

FooContainer::FooContainer(const FooContainer& fc)
{
many=fc.many;

}

will attempt to copy the pointers and will be (thankfully) disallowed by the compiler for unique_ptr. so I would need to do something like this

FooContainer::FooContainer(const FooContainer& fc)
{
many.reserve(fc.many.size());
for(int i=0;i<fc.many.size();i++)
many.emplace_back(new Foo(*fc.many[i]));//assume that Foo has a copy constructor
}

Is this the way to do it? Or may be I should use shared_ptr instead of unique_ptr?

I also have an additional question.
The reason to go for smart pointers (and also for protected in the code above) is that I have derived class BarContainer that aggregates objects Bar in many, which are in turn subclass of Foo. Since the the handling of Bar is very similar to Foo this approach will allow to save a lot of duplicate code compared to two separate classes.

However,. the copy constructor of the BarContainer is problematic. It will the call copy constructor of FooContainer, that will go agead and copy only the Foo part instead of whole Bar. even worse, any invocation of the virtual methods of Bar will call the version of Foo. So I need a way to override this behaviour.Making the copy constructor virtual is not possible. Also the copy constructor of Bar could discard the result of Foo copy constructor and to dperform correct copying, but this is quite inefficient

So what is the best solution for this problem?

1条回答
地球回转人心会变
2楼-- · 2020-03-05 07:19

Or may be I should use shared_ptr instead of unique_ptr?

That depends on whether you require deep copies or are okay with shallow copies (meaning changes to one will also be visible in the other).

However,. the copy constructor of the BarContainer is problematic. It will the call copy constructor of FooContainer, that will go agead and copy only the Foo part instead of whole Bar.

The usual fix is to give your base class a virtual method clone:

class Foo {
public:
    Foo(Foo&&) = default;
    Foo& operator=(Foo&&) = default;
    virtual ~Foo() = 0;
    virtual std::unique_ptr<Foo> clone() const = 0;

protected: // or public if appropriate
    Foo(const Foo&);
    Foo& operator=(const Foo&);
};

class Bar : public Foo {
public:
    virtual std::unique_ptr<Foo> clone() const;
};

std::unique_ptr<Foo> Bar::clone() const {
    return make_unique<Bar>(*this);
}

If Foo is not abstract, it would also have an actual implementation of clone().

FooContainer::FooContainer(const FooContainer& fc)
{
    many.reserve(fc.many.size());
    for (auto const& fptr : fc.many)
        many.emplace_back(fptr->clone());
}

I've used a template function make_unique, which was accidentally forgotten from the C++11 Standard, but will be official soon. If your compiler doesn't have one, it's simple to put your own in some header file:

template <typename T, typename... Args>
std::unique_ptr<T> make_unique(Args&& ... args) {
    return std::unique_ptr<T>( new T(std::forward<Args>(args)...) );
}

(Together, unique_ptr, make_unique, shared_ptr, make_shared, and vector finish the huge language improvement meaning you'll almost never need the low-level and dangerous new or delete keywords again.)

查看更多
登录 后发表回答