1

I have the following set up:

class Base {
private:
// data members
public:
// methods
// pure virtual methods
virtual Base* clone() const =0;
}

class Derived : public Base{
private:
// more data members
public:
// more methods
// pure virtual methods overridden
Derived* clone() const override{
return new Derived(*this);
}

I now want to introduce a new derived class which, in the context of a decorator pattern, has a unique pointer to a Base class object as a data member. My question is then how to properly implement the clone() method because the "standard" implementation leads to a compile time error as the unique pointer can't be copied:

class DecoratedDerived : public Base{
private:
unique_ptr<Base> ptr;
// more data members
public:
// more methods
// pure virtual methods overridden
DecoratedDerived* clone() const override{
return new DecoratedDerived(*this); // compiler error
}

One solution would be to just construct a new DecoratedDerived class object in the clone method (by explicitly deep copying all the members associated to the current object) and then passing a pointer to this. However this is quite time consuming if the class has a lot of other members.

I should also say that I am only using a unique pointer because this seems the standard smart pointer to use in modern C++. In particular before I used C+11 I had just designed my own smart pointer which took care of all the memory management etc. so that for that type of smart pointer there would be no issue with the clone method in the decorated class.

Thanks in advance for your help!

twj
  • 35
  • 6
  • 1
    `unique_ptr is` the standard smart pointer for some cases. `shared_ptr` is the standard smart pointer for other cases. The answer to your question depends on what you want the semantics of clone to be, should a clone point to the origina BAse object or a new one – pm100 Apr 15 '22 at 16:24
  • 2
    just implement copy constructor which will clone object pointed by `ptr`. – Marek R Apr 15 '22 at 16:25
  • 2
    The simplest way would be for your class to supply a copy constructor that correctly copies the `unique_ptr` (e.g. clones the object that the `unique_ptr` manages). Then your `clone()` member function, as over-ridden, would work. – Peter Apr 15 '22 at 16:27
  • Does `DecoratedDerived` own (control the life-time) of the `Base` object pointed to by `unique_ptr ptr;` ? – Richard Critten Apr 15 '22 at 16:31

2 Answers2

0

Here is possible implementation, when new DecoratedDerived is constructed member item is cloned too:

class Base {
   public:
    virtual std::unique_ptr<Base> clone() const = 0;
};

class Empty : public Base {
   public:
    std::unique_ptr<Base> clone() const override {
        return std::make_unique<Empty>();
    }
};

class DecoratedDerived : public Base {
   private:
    std::unique_ptr<Base> ptr;

   public:
    DecoratedDerived() : ptr{std::make_unique<Empty>()} {}

    DecoratedDerived(std::unique_ptr<Base> wrap) : ptr{std::move(wrap)} {}

    std::unique_ptr<Base> clone() const override {
        return std::make_unique<DecoratedDerived>(ptr->clone());
    }
};

https://godbolt.org/z/sG3zTehb1

Empty class is a nice way to avoid checking for nullptr.

Note also since you are using unique_ptr I've introduce them in other places when it seems logical.

Marek R
  • 32,568
  • 6
  • 55
  • 140
0

Simply add a copy constructor to DecoratedDerived that clone()'s the data member, eg:

class DecoratedDerived : public Base {
private:
    unique_ptr<Base> ptr;
    // ...
public:
    DecoratedDerived(const DecoratedDerived &src)
        : Base(src), ptr(src.ptr ? src.ptr->clone() : nullptr)
    {
    }

    // ...

    DecoratedDerived* clone() const override{
        return new DecoratedDerived(*this);
    }
};

Online Demo

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • Hello, thanks for your answer. This is what I did in the end. I also have a follow up: don't we also need to check for the null ptr in the clone() method? – twj Apr 20 '22 at 10:09
  • I should say, my follow up question is a general one (although one which I was lead to by your answer). In particular I understand that there is a check for null ptr in the copy constructor, but I am talking about a check for null more generally in the clone method when used outside of the copy constructor (I think one needs a check because the clone() involves the dereferencing operator). – twj Apr 20 '22 at 10:25
  • "*don't we also need to check for the null ptr in the clone() method?*" - Not inside of clone() itself, no. If you call clone() on a null object pointer, thus making `this` null, then you have other errors in your code that you need to deal with. – Remy Lebeau Apr 20 '22 at 14:50