理论上而言,当 C++提供了std::unique_ptr
, C++的程序就不应该出现普通指针了。所有普通指针都可以用std::unique_ptr
代替,避免手动删除对象。
std::unique_ptr<int> x1 = new int{1};
std::unique_ptr<int[10]> x2 = new int[10]{2, 3, 4};
std::unique_ptr
是一个标准的RAII
实现,内部只维护了一个指针。因此它没有任何效率问题。
它的实现也特别简单直接。唯一的问题时,当删除原始资源时,需根据数据是单个数据还是数组,决定调用delete
还是delete[]
。这通过定义两个不同的Deleter
来实现:
template<typename _Tp>
struct default_delete {
void operator()(_Tp* __ptr) const {
delete __ptr;
}
};
template<typename _Tp>
struct default_delete<_Tp[]> {
template<typename _Up>
typename enable_if<is_convertible<_Up(*)[], _Tp(*)[]>::value>::type operator()(_Up* __ptr) const {
static_assert(sizeof(_Tp)>0, "can't delete pointer to incomplete type");
delete [] __ptr;
}
};
然后再对两种情况分别定义unique_ptr
,下面只列其中一个:
template <typename _Tp, typename _Dp = default_delete<_Tp>>
class unique_ptr {
std::tuple<pointer, _Dp> _M_t;
public:
~unique_ptr() noexcept {
auto& __ptr = _M_t.get<0>();
if (__ptr != nullptr) {
_M_t.get<1>(__ptr);
_M_t.get<0>() = nullptr;
}
}
}
这里有一个很有意思的地方在std::tuple<pointer, _Dp>
。一个很自然的想法是为什么不定义成下面这样,用起来更方便:
template <typename _Tp, typename _Dp = default_delete<_Tp>>
class unique_ptr {
pointer _M_ptr;
_Dp _M_dp;
}
其原因是默认的_Dp
是一个空结构。如果按照上面这种写法,sizeof(unique_ptr)
就变成了 16。而用std::tuple
,那么sizeof(unique_ptr)
只有 8。这个差异非常大。
为确定这一点,我们可以运行下面这段测试程序:
#include <iostream>
#include <memory>
struct Empty { };
struct A {
double x;
Empty e;
};
int main() {
std::cout << sizeof(std::unique_ptr<int>) << std::endl; // 8
std::cout << sizeof(std::shared_ptr<int>) << std::endl; // 16
std::cout << sizeof(Empty) << std::endl; // 1
std::cout << sizeof(A) << std::endl; // 16
std::cout << sizeof(std::tuple<double, Empty>) << std::endl; // 8
}
其原因需要看std::tuple
的具体实现。
Q. E. D.