auto_ptr作为最早的智能指针,可以实现以RAII手法管理堆区对象,但它设计的本意只是简单的利用C++对于栈区对象的自动析构管理堆区对象,
并不像shared_ptr那样包含引用计数,可以在每次拷贝的时候多出一个“分身”。这时候,拷贝的语义就成了很大的问题(按理说直接禁掉可能好好些),
于是就出现了下面这个不伦不类的原型:
explicit auto_ptr (X* p=0) throw(); auto_ptr (auto_ptr& a) throw(); template<class Y> auto_ptr (auto_ptr<Y>& a) throw(); auto_ptr (auto_ptr_ref<X> r) throw();
auto_ptr& operator= (auto_ptr& a) throw(); template <class Y> auto_ptr& operator= (auto_ptr<Y>& a) throw(); auto_ptr& operator= (auto_ptr_ref<X> r) throw();
这个跟一般我们定义一个类的拷贝(构造和赋值)函数就不一样了:
class foo { foo(const foo& a); foo& operator=(const foo& a); }
关键在于少了const,而每当auto_ptr被拷贝,它都会被置为null,相当于“移动”的语义。
这样不仅违反直觉,而且在C++11里有了正统的移动语义的情况下更显得奇怪,于是重新设计了unque_ptr
,改动不大,只是把语义纠正过来了,
default (1) constexpr unique_ptr() noexcept; from null pointer (2) constexpr unique_ptr (nullptr_t) noexcept : unique_ptr() {} from pointer (3) explicit unique_ptr (pointer p) noexcept; from pointer + lvalue deleter (4) unique_ptr (pointer p, typename conditional<is_reference<D>::value,D,const D&> del) noexcept; from pointer + rvalue deleter (5) unique_ptr (pointer p, typename remove_reference<D>::type&& del) noexcept; move (6) unique_ptr (unique_ptr&& x) noexcept; move-cast (7) template <class U, class E> unique_ptr (unique_ptr<U,E>&& x) noexcept; move from auto_ptr (8) template <class U> unique_ptr (auto_ptr<U>&& x) noexcept; copy (deleted!) (9) unique_ptr (const unique_ptr&) = delete;
可以看到,拷贝操作直接被禁掉了。
在应用方面,auto_ptr由于奇怪的拷贝语义,导致在容器中使用的话很容易出错,比如下面的代码:
vector<auto_ptr<int>> foo; ... auto item = foo[0];
容器中的元素不知不觉就被改掉了(置为null)。
如果是unique_ptr,就看得很清楚了:
vector<unique_ptr<int>> foo; ... auto item = std::move(foo[0]);
这样也算是更改了容器,但是加上std::move之后(不加会错,因为拷贝被禁用了),代码的意图明显多了。