3

使用 std::unique_ptr 代替原始指针

 3 years ago
source link: https://zhiqiang.org/coding/std-unique-ptr.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

使用 std::unique_ptr 代替原始指针

作者: 张志强

, 发表于 2019-10-14

, 共 2093 字 , 共阅读 191 次

理论上而言,当 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.


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK