-1

Suppose that I have a class B.
B is a container of a single field C c;.
C is designed to be a base class. It is the super class of C1,C2, etc.

class C{
    public: int dummy=3;
    public: virtual void print(){ std::cout<<"I am C"<<std::endl; }
};
class C2: public C{
    public: virtual void print(){ std::cout<<"I am C2"<<std::endl; }
};
class B{
    public: ??? c;  //C or C* or std::unique_ptr or ....
};  

Question

Which is a suitable data type of a light-weight field c that :-

(#1) c can also hold an instance of a derive class e.g. C2.
(#2) The field c is automatically copied by value when calling B::operator=() or default B's copy constructor, so no custom functions are required.
(#3) c is automatically deleted when B is deleted, so no B's custom destructor is required.

Roughly speaking, here is what I expect :-

int main(){
    B b1;
    b1.c = C2();    //(#1)
    B b2=b1;        //  b2.c should be another copy of b.c 
    b2.c.dummy = 3;   //(#2) b.c should not be effected    
    //(#3) both b1.c and b2.c are deleted, no memory leak 
}

It is very useful for prototyping (prototype-pattern), where I want to copy many objects and customize each of them with high flexibility and minimum human-error (because no need to maintain either copy-constructor or operator=()).

My poor solutions

Version 1 (C*)

#include <iostream>
class C{
    public: virtual void print(){ std::cout<<"I am C"<<std::endl; }
};
class C2: public C{
    public: virtual void print(){ std::cout<<"I am C2"<<std::endl; }
};
class B{
    public: C* c;
};    
int main(){
    B b1;
    b1.c = new C2(); 
    B b2=b1;   
}

The approach does not meet the requirement :-

  • violate (#2): In B b2=b;, b.c is shallow copy. I want deep copy.
  • violate (#3): I have to delete b.c manually, or add such statement in B's destructor.

Version 2 (std::unique_ptr)

class B{
    public: std::unique_ptr<C> c=nullptr;
};    
int main(){
    B b1;
    b1.c = std::make_unique<C2>();
    B b2=b1;
}

This is not compilable because default B::operator=() no longer work.
I have to manually code B::operator=().
I don't want to code it manually, because it is (human-)error-prone. (violate (#2))

Version 3 (new customize class)

Encapsulate C inside a custom class that do exactly what I want.

Here is a draft :-

template<class T> CrazyCopy{
    T* t;
    // write some custom operator=, custom constructor, custom destructor
};
class B{
    public: CrazyCopy<C> c;
};

I think it is overkill. (?)

javaLover
  • 6,347
  • 2
  • 22
  • 67
  • Generally this is done using a `clone` function. You should be able to find lots of results on that. – NathanOliver Feb 07 '17 at 13:21
  • Don't think of the new smart pointers as "auto-deleting pointers" but more in terms of *ownership*. Will an instance of `B` *own* the only instance of `C`? Or will the ownership of the `C` instance be *shared* (for example between multiple instances of `B`)? It seems to me that you want to share the ownership of `C`, in which case you should use `std::shared_ptr` instead. Alternatively, that you should *clone* the `C` instance, in which case `std::unique_ptr` is what you should use (together with some operator overloading and `clone` member functions). – Some programmer dude Feb 07 '17 at 13:21
  • @NathanOliver As far as I know, I have to manually code the clone function ... field by field (?) – javaLover Feb 07 '17 at 13:23
  • shared_ptr won't give a deep copy. I think `CrazyCopy` is the way to go (but it's going to be tricky - you need to capture a pointer to the appropriate assignment operator and copy constructor). ... Oh, you edited the comment :-) – Martin Bonner supports Monica Feb 07 '17 at 13:24
  • @javaLover Yes. You make it a virtual function and then in your copy constructor of the class that holds the polymorphic object you call the clone function to maker the copy. – NathanOliver Feb 07 '17 at 13:25
  • 1
    Since you are dealing with dynamic polymorphism via `C *c;` so you need to manually handle the copy creation and deletion. – sameerkn Feb 07 '17 at 13:26
  • @NathanOliver That would cause human error, especially when I add a new field, and I forget to edit the clone. :( – javaLover Feb 07 '17 at 13:26
  • The `clone` function can use a copy constructor internally. – Martin Bonner supports Monica Feb 07 '17 at 13:26
  • @javaLover As Martin pointed out it is easily fixed by using the copy constructor in the clone function. – NathanOliver Feb 07 '17 at 13:28
  • @NathanOliver `clone` is new for me. If I do it, then I add another field `d` that work like `c` inside `B`, I will have to edit the clone function, right? Can I avoid human-error? – javaLover Feb 07 '17 at 13:29
  • @javaLover If you have the clone function use the copy constructor then you only need to make sure the copy constructor is right and you would have to do that anyway if you use a custom one and if you use the compiler built one then you do not have to worry about this. see this [answer](http://stackoverflow.com/a/5148751/4342498) – NathanOliver Feb 07 '17 at 13:32
  • I'm curious, what are the circumstances that do not allow you to define the operator and the copy constructor manually? – Aziuth Feb 07 '17 at 16:09
  • @Aziuth I want to avoid human error. If I define the functions, I will have to control each field manually. Suppose that I manually code them today .... a year later, I add a new field `d` in `B` that is need to be (customary) copied ... I will have to add a line (to copy `d`) in those functions manually. I might forget to do it. ... If my logic is wrong, please criticize. ... Now, I am reading about `clone`, as experts suggested. It may tell how my logic is wrong. – javaLover Feb 08 '17 at 02:21
  • Well, that case is avoided, true. But you won't be able to do so in every case, so the important thing to do here would be to find methods how to not forget such things, right? Like documentation on how to extend, unit tests et cetera. That said, of course doing both is better, yeah. In any case, readability should have higher priority. Although the copy pointer concept is pretty much readable, so no problem here. – Aziuth Feb 08 '17 at 10:43
  • @Aziuth That is a valuable opinion, thank! – javaLover Feb 08 '17 at 10:55

2 Answers2

1

I think if you want that particular combination of behavior, you do need a custom pointer. Try this.

template<class T>
class CrazyCopy {

    public:

        CrazyCopy<T>() : t_(nullptr) {}

        CrazyCopy<T>(T* const t) : t_(t) {}

        CrazyCopy<T>(const CrazyCopy<T>& t) : t_(new T(*t.t_)) {}

        CrazyCopy<T>(CrazyCopy&& t) : t_(t.t_) {
            t.t_ = nullptr;
        }

        ~CrazyCopy<T>() {
            delete t_;
        }

        CrazyCopy<T>& operator=(const CrazyCopy<T>& t) {
            delete t_;
            t_ = new T(*t.t_);
        }

        CrazyCopy<T>& operator=(CrazyCopy<T>&& t) {
            t_   = t.t_;
            t.t_ = nullptr;
        }

        T& operator*() const {
            return *t_;
        }

        T* Get() const {
            return t_;
        }

        void Reset(T* const t) {
            delete t_;
            t_ = t;
        }

    protected:

        T* t_;

};
Sam Marinelli
  • 999
  • 1
  • 6
  • 16
  • 1
    Wrapping a `std::unique_ptr` would make this code that much more robust. In any case, there's a pretty bad case of implicit object slicing inside your copy constructor. – Quentin Feb 07 '17 at 15:35
  • I agree with Quentin. This is the first answer and it is useful as a rough idea, though. Thank! – javaLover Feb 08 '17 at 04:55
1

The custom class is the way to go. We do have to take care of object slicing, so some type erasure is needed for the copy operation. A first stab at it might look like this:

template <class T>
struct copy_ptr {

    copy_ptr() = default;

    copy_ptr(copy_ptr const &orig)
    : _ptr{orig._cpy(*orig._ptr)}
    , _cpy{orig._cpy} { }

    template <class U>
    copy_ptr(std::unique_ptr<U> ptr)
    : _ptr{std::move(ptr)}
    , _cpy{[](T const &obj) -> std::unique_ptr<T> {
        return std::make_unique<U>(static_cast<U const &>(obj));
    }} { }

    // Assignment and move operations left as an exercise :)

private:
    std::unique_ptr<T> _ptr;

    using Copier = std::unique_ptr<T> (*)(T const &);
    Copier _cpy = [](T const &){ return std::unique_ptr<T>{}; };
};

When initializing a copy_ptr<T> with a unique_ptr<U>, with U derived from T, we generate and store the cloning function, which is then used to perform copies.

See it live on Coliru

Quentin
  • 62,093
  • 7
  • 131
  • 191
  • `Copier` trick is so beautiful. **1.** May you also express some (negative) opinion about my question, please? I got downvotes = bad practice? Should I use [**clone()**](http://stackoverflow.com/a/5148751/3577745), or [**CRTP**](https://katyscode.wordpress.com/2013/08/22/c-polymorphic-cloning-and-the-crtp-curiously-recurring-template-pattern/) instead? **2.** I think your solution is cleaner and require less maintenance cost. Why people still use `clone` or `CRTP`? **3.** What are disadvantages if I use `copy_ptr` widely in my program? Performance penalty? – javaLover Feb 08 '17 at 05:02
  • 1
    @javaLover About your question, I don't really know. If anything it's a bit bulky, but there are worse ones. It *is* about well-trodden ground (how to copy an object polymorphically), so maybe people considered it as a lack of research, but it looks fine to me. – Quentin Feb 08 '17 at 09:00
  • 1
    @javaLover as a disadvantage, this solution has to use an additional function pointer to store the copier, typically doubling the size of the smart pointer. As an advantage, it works from the outside: you can store any object without modification. We can work around the additional function pointer by using a class that wraps the object and provides a `clone` function, and keep a pointer to that -- essentially autogenerating the Clone implementation. This'd also benefit from a factory function to construct the wrapped objects directly. – Quentin Feb 08 '17 at 09:06
  • 1
    @javaLover the Clone pattern, on the other hand, is technically much simpler: implement `clone` in your classes, then you can just call `foo.clone()` and get a copy. This requires modifying all of the classes, but does not require type erasure, and doesn't become atrociously ugly in languages without operator overloading and smart pointers (remember that Clone is a GoF pattern, designed to work with most OOP languages at the time). CRTP is tangential to the discussion: it's a great way to implement Clone in C++ thanks to templates, but is not required. – Quentin Feb 08 '17 at 09:10
  • 1
    @javaLover finally, I recommend to you [this short (20min) presentation](https://channel9.msdn.com/Events/GoingNative/2013/Inheritance-Is-The-Base-Class-of-Evil), where Sean Parent advocates doing polymorphism "from the outside", using templates and type erasure instead of the usual explicit interfaces and virtual functions. Not a reason to throw classical polymorphism out the window right away, but a very interesting demonstration of what is possible with templates and duck-typing. – Quentin Feb 08 '17 at 09:17