对unique_ptr与shared_ptr为nullptr时, 删除器的调用机制疑惑

先贴上代码:

#include <memory>
#include <iostream>

auto unique_deleter = [](int* p) {
    std::cout << "unique_ptr delete:" << *p << "\n";
    delete p;
};

auto shared_deleter = [](int* p) {
    std::cout << "shared_ptr delete:" << *p << "\n";
    delete p;
};

auto shared_deleter_check_nullptr = [](int* p) {
    if (p != nullptr)
    {
        std::cout << "shared_ptr2 delete:" << *p << "\n";
        delete p;
    }
    else
    {
        std::cout << "shared_ptr2 is null\n";
    }
};


int main()
{
    {
        // unique_ptr == nullptr, 删除器没有被调用
        std::unique_ptr<int, decltype(unique_deleter)> pUniquePtr(nullptr, unique_deleter);

        // unique_ptr != nullptr, 删除器被调用
        std::unique_ptr<int, decltype(unique_deleter)> pUniquePtr2(new int(5), unique_deleter);

        // shared_ptr == nullptr, 删除器仍然被调用, 幸好对"nullptr"有检查
        std::shared_ptr<int> pSharedPtr(nullptr, shared_deleter_check_nullptr);

        // shared_ptr != nullptr, 删除器被调用
        std::shared_ptr<int> pSharedPtr2(new int(10), shared_deleter);

        // shared_ptr == nullptr, 删除器仍然被调用, 没有对"nullptr"的检查, 程序崩溃
        //std::shared_ptr<int> pSharedPtr(nullptr, shared_deleter);
    }

    getchar();
}

测试环境: win10 64bit g++4.9.2 –std=c++1y
通过测试发现, 当std::unique_ptr为nullptr时, 自定义删除器不会被调用, 而std::shared_ptr为nullptr时, 自定义删除器仍然会被调用, 这就要求, 当定义ared_ptr的删除器时,
需要对is nullptr作检查, 而unique_ptr就不需要, Why?

回答:

野男孩 2016.09.07 21:34

区别在于,shared_ptr对象在析构时,内部有个引用计数对象,shared_ptr对象析构时调用的是引用计数对象的dec()类似的函数。这个函数时在构造时你指定了deleter。所以发现计数为0时调用的是引用计数对象中记录的那个你传入的自定义deleter。

unique_ptr对象析构时,是正经调用析构函数。标准规定了不是nullptr才调用deleter:Effects: If get() == nullptr there are no effects. Otherwise get_deleter()(get()).
好像shared_ptr这部分没说太清楚。标准里这一段给你摘出来吧:
A shared_ptr object is empty if it does not own a pointer.
这里的empty是特指的constexpr shared_ptr(nullptr_t) : shared_ptr() { }

而你的代码里给定了deleter, 所以调用的是template shared_ptr(nullptr_t p, D d),这样的话对象就不是empty了。

不是empty的话,标准里关于析构的定义就很明确:
Effects:
— If *this is empty or shares ownership with another shared_ptr instance (use_count() > 1), there are no side effects.
— Otherwise, if *this owns an object p and a deleter d, d(p) is called.
— Otherwise, *this owns a pointer p, and delete p is called.

总结

明白了, 就像”野男孩”说的, shared_ptr 的”is empty与”is null”是两回事

if shared_ptr.use_count > 0 then shared_ptr is not empty
else shared_ptr is empty;

if shared_ptr.data () != nullptr then shared_ptr is not null
else shared_ptr is null

shared_ptr删除器调用的判断条件是”shared_ptr 是否 empty”, 而不是”shared_ptr 是否 null”,
通过代码测试:

(1) shared_ptr p1(nullptr); // use_count == 0, shared_ptr is null and is not empty
(2) shared_ptr p2(nullptr, deleter); // use_count == 1, shared_ptr is null, but is not empty
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
下面是一个简单的 shared_ptr 的实现,供参考: ```c++ template<typename T> class shared_ptr { public: shared_ptr() : ptr_(nullptr), ref_count_(nullptr) {} explicit shared_ptr(T* ptr) : ptr_(ptr) { ref_count_ = new int(1); } shared_ptr(const shared_ptr<T>& other) : ptr_(other.ptr_), ref_count_(other.ref_count_) { ++(*ref_count_); } ~shared_ptr() { release(); } shared_ptr<T>& operator=(const shared_ptr<T>& other) { if (this != &other) { release(); ptr_ = other.ptr_; ref_count_ = other.ref_count_; ++(*ref_count_); } return *this; } T* get() const { return ptr_; } void reset() { release(); ptr_ = nullptr; ref_count_ = nullptr; } bool operator==(const shared_ptr<T>& other) const { return ptr_ == other.ptr_; } bool operator!=(const shared_ptr<T>& other) const { return !(*this == other); } T& operator*() const { return *ptr_; } T* operator->() const { return ptr_; } int use_count() const { return *ref_count_; } private: void release() { if (ref_count_ && --(*ref_count_) == 0) { delete ptr_; delete ref_count_; } } private: T* ptr_; int* ref_count_; }; ``` 这个实现中,我们使用一个计数 ref_count_ 来记录当前有多少个 shared_ptr 共享一个对象。每当有一个新的 shared_ptr 被创建,计数会加一。每当一个 shared_ptr 被销毁,计数会减一。只有当计数为 0 ,才会销毁对象。在拷贝构造函数和赋值操作符中,我们需要注意将计数加一。在析构函数中,我们需要注意将计数减一,并在计数为 0 销毁对象和计数

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值