2 liuhhaiffeng liuhhaiffeng 于 2016.09.07 20:30 提问

对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? Why? Why?

3个回答

coding_hello
coding_hello   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()).

coding_hello
coding_hello   2016.09.07 21:43

好像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.

liuhhaiffeng
liuhhaiffeng   2016.09.08 21:05

明白了, 就像"野男孩"说的, 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 the 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

Csdn user default icon
上传中...
上传图片
插入图片
准确详细的回答,更有利于被提问者采纳,从而获得C币。复制、灌水、广告等回答会被删除,是时候展现真正的技术了!
其他相关推荐
C++ - 智能指针(smarter pointer)自定义删除器(deleter) 的方法 详解 及 代码
智能指针(smarter pointer)自定义删除器(deleter) 的方法 详解 及 代码   版权所有, 禁止转载, 如有需要, 请站内联系 本文地址: http://blog.csdn.net/caroline_wendy   智能指针包含两种"shared_ptr"和"unique_ptr", 由于两种指针的实现方式不同, 所以传递删除器的方式也不同; "shared_pt
浅析shared_ptr 和weak_ptr、定制删除器
shared_ptr的作用如同指针,但又比指针更加安全,甚至还能提供基本的线程安全保证。记录了有多少个shared_ptrs共同指向一个对象(即引用计数),它基本上解决了在使用c++开发过程中不可避免的使用指针而遇到的许多问题,例如:内存泄漏和内存的提前释放,还有由于指针内存申请而产生的异常问题等。shared_ptr指针解决了auto_ptr和一旦最后一个这样的指针被销毁,也就是一旦某个对象的引
关于shared_ptr模板和unique_ptr模板的实现
在c++primer16.1这一节有这样的题目:     让我们实现自己的shared_pr和unique_ptr模板      如果没有删除器的传入,那么这两个模板的实现并不算难,shared_ptr共享指针,只需要为它多申请一块内存用来存放引用次数就可以了。 但是在这一节介绍了shared_ptr和unique_ptr传入删除器的不同点,如果要加入删除器的传入,那就是个很大的挑战了。
c++定制删除器
定制删除器其实是利用仿函数 一、仿函数是什么? 不是函数但可以像函数一样使用,因为重载了operator() 简单举例: template struct Less { bool operator()(const T& l, const T& r) { return l < r; } }; void Test2() { Lessless1; cout << less1(1,
C++11中使用shared_ptr和unique_ptr管理动态数组
在C++11中,若使用shared_ptr管理一个动态数组,则需手动制定一个删除器。 auto sp = std::shared_ptr(new int[len], [](char *p){delete []p;}); 但是这样使用有点麻烦,经过查阅资料,发现可以使用shared_ptr为动态数组创建一个工厂函数。 具体使用如下: #include #include #includ
shared_ptr之循环引用&定置删除器
shared_ptr虽然方便,但是它有着一个致命的缺陷就是循环引用问题,因为shared_ptr本身并没有能力解决这个问题,所以我们又引入了弱指针weak_ptr来辅助shared_ptr解决这个问题。 那么循环引用又是什么场景? 举个栗子: 假设现在我们要创建一个双向整形链表,但是这个链表的指针域全部都用shared_ptr维护: struct Node {     int
在NDK中使用shared_ptr
在NDK中使用shared_ptr
实战c++中的智能指针unique_ptr系列-- unique_ptr的get_deleter方法(自定义删除器)
unique_ptr的成员函数在上一篇博客中几乎全部涵盖,其实还有一个很有踢掉,即std::unique_ptr::get_deleter字面已经很明显了,就获得deleter: Returns the stored deleterThe stored deleter is a callable object. A functional call to this object with a sin
shared_ptr:定制删除器 和 循环引用
前面我们介绍智能指针的时候,说了两个智能指针分别是:auto_ptr和scoped_ptr,auto_ptr有问题,为此,scoped_ptr的出现解决了这个问题,scoped_ptr太霸道,不允许别人和他共用一块内存空间,所以,我们还得想办法解决这个问题。回想我们以前看过内容,当提到共用一块内存空间的时候,我们会想到什么?当然是深拷贝和浅拷贝了,最后我们是不是有给出了一个写时拷贝,就是通过引用计数
实战c++中的智能指针unique_ptr系列--通过unique_ptr对shared_ptr进行初始化
首先需要明确的是,这篇文章不是要描述unique_ptr和shared_ptr两个只能指针之间的区别,主要就是为了用unique_ptr对shared_ptr进行初始化。#include <iostream> #include <memory>int main() { std::cout << "start!\n"; auto customArrayAllocator = [](un