假设我有一个类B
B
是单个字段C c;
的容器C
被设计成一个基类。它是C1
、C2
等的超类
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-constructor
或operator=()
)。
我糟糕的解决方案
版本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_;
};