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



假设我有一个类B
B是单个字段C c;的容器
C被设计成一个基类。它是C1C2等的超类

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还可以保存派生类的实例,例如C2
(#2)当调用B::operator=()或默认B的复制构造函数时,字段c会按值自动复制,因此不需要自定义函数
(#3)删除B时会自动删除c,因此不需要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):B b2=b;中,b.c是浅拷贝。我想要深度复制
  • 违反(#3):我必须手动删除b.c,或者在B的析构函数中添加这样的语句

版本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(新的自定义类)

将C封装在一个自定义类中,该类正是我想要的。

这是一份草案:-

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

我认为这太过分了。(?)

自定义类是一种方法。我们必须处理对象切片,因此复制操作需要一些类型擦除。第一次尝试可能是这样的:

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>时,使用从T派生的U,我们生成并存储克隆函数,然后用于执行复制。

在Coliru 上实时观看

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

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

最新更新