适当类型的非静态字段,自动按值复制=并支持多态性

Appropriate type of non-static field that automatic copy= by value and support polymorphism

假设我有一个 class B.
B 是单个字段的容器 C c;.
C 被设计为基数 class。是C1C2等的超级class

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 ....
};  

问题

哪个是适合的轻量级字段的数据类型 c :-

(#1) c 也可以包含派生 class 的实例,例如C2.
(#2)字段c在调用B::operator=()或默认B的复制构造函数时自动按值复制,所以没有自定义函数必填。
(#3) c在删除B时自动删除,所以不需要B的自定义析构函数。

粗略地说,这是我的期望:-

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 
}

这对于原型制作(原型模式)非常有用,我想复制许多对象并以高度灵活性和最小的人为错误自定义每个对象(因为不需要维护 copy-constructoroperator=()).

我糟糕的解决方案

版本 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;   
}

方法不符合要求:-

版本 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;
}

这是不可编译的,因为默认 B::operator=() 不再有效。
我必须手动编码 B::operator=().
我不想手动编码,因为它很容易(人为)出错。 (违反(#2))

版本 3(新定制 class)

将 C 封装在自定义 class 中,完全按照我的要求进行。

这是草稿:-

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

我认为这是矫枉过正。 (?)

我认为如果您想要特定的行为组合,您确实需要一个自定义指针。试试这个。

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_;

};

习惯 class 是必经之路。我们确实必须处理对象切片,因此复制操作需要一些类型擦除。第一次尝试它可能看起来像这样:

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>{}; };
};

当用 unique_ptr<U> 初始化 copy_ptr<T> 时,UT 派生,我们生成并存储克隆函数,然后用于执行复制.

See it live on Coliru