前文提到了,auto_ptr由于资源转移机制设计的不合理,有可能导致程序崩溃。在C++11中,提出了unique_ptr用以取代auto_ptr。
一、unique_ptr的设计思想
unique_ptr的设计思想和auto_ptr整体上是一致的,也是由类模板托管指针。对于资源的管理模式和auto_ptr几乎一致:一个指向堆内存的指针,最多只能同时被一个unique_ptr托管。
那么unique_ptr改进点在哪里?其核心思想就是:程序员想对不同的unique_ptr对象进行资源转移,编译器不限制。但是编译器只允许将右值unique_ptr对象的资源转移给其他对象。相当于给程序员提了个醒:这个智能指针是一个右值,一个将亡值,它的资源已经给出去了,你不要在使用它了。当然了,这也仅仅是提醒的作用,如果程序员仍然一意孤行要使用右值,那么程序崩溃是不可避免的。
相对于auto_ptr,unique_ptr主要做了这两件事情:
(1)禁用拷贝构造函数,和参数为左值的赋值运算符重载函数。
(2)实现移动构造函数,和参数为右值的赋值运算符重载函数。
多说一句,unique_ptr相对于auto_ptr,支持了对数组的托管,但是个人感觉这个功能过于鸡肋,不再赘述。
二、自己实现的unique_ptr
有了基本思想,我们大可以自己实现一个unique_ptr:
template<typename Ty> //Ty是被托管的指针类型
class my_ptr{
private:
Ty * ty; //被托管的指针
public:
explicit my_ptr(Ty * ty1=0):ty(ty1){}
~my_ptr(){delete ty;} //析构函数中释放资源
Ty * get(){ //返回被托管的指针
return ty;
}
Ty * release(){ //转移被托管的指针
Ty * temp = ty;
ty = 0;
return temp;
}
void reset(Ty * ty2){ //重置被托管的指针
delete ty;
ty = ty2;
}
Ty * operator ->(){ //重载->和*运算符,让智能指针用起来像指针
return ty;
}
Ty & operator * (){
return *(ty);
}
my_ptr(my_ptr && ty1){ //移动构造函数
this->ty = ty1.release();
}
my_ptr& operator = (my_ptr && ty1 ){ //移动赋值运算符重载
reset(ty1.release());
return *this;
}
my_ptr& operator = (my_ptr & ty1 ) = delete; //禁用左值赋值运算符重载
my_ptr(my_ptr & ty1) = delete; //禁用左值拷贝构造函数
};
具体用法:
class Test {
public:
Test() { cout << "Test start..." << endl; }
~Test() { cout << "Test end..." << endl; }
int getDebug() { return this->debug; }
private:
int debug = 20;
};
void my_ptr_test(my_ptr<Test> ptr) {}
int main ( )
{
my_ptr<Test> ptr1(new Test);
//my_ptr<Test> ptr2(ptr1); 错误,拷贝构造函数被禁用了
my_ptr<Test> ptr2(move(ptr1));
my_ptr<Test> ptr3;
//ptr3 = ptr2; 错误,左值运算符重载被禁用了。
ptr3 = move(ptr2);
//my_ptr_test(ptr3); 错误,不允许以左值的形式给形参赋值
my_ptr_test(move(ptr3));
cout<< ptr3.get();
}
但是,这个解决方案还是略显治标不治本。只是通过一个move函数提醒程序员,慎重考虑资源转移的问题。根本原因是前面两种智能指针的设计模式就是资源独占。那么有没有更好的解决方法?请看下一篇。