浅谈智能指针工作原理(std::shared_ptr、std::unique_ptr、std::weak_ptr、std::auto_ptr)

文章目录

前言

智能指针,老有人会问关于对它的认识。为了很好的回答这个问题。对智能指针 std::shared_ptr、std::unique_ptr、std::weak_ptr和 std::auto_ptr进行分析。在此做一个总结,对于智能指针的分析告一段落。

一、个人理解

智能指针,之所以称为智能,是因为它拥有对内存的自动回收机制。其实这一原理,是利用c++的特性。创建一个对象,当此对象的生命周期结束时,会调用类的析构函数。在析构函数内,实现将所管理的内存释的操作。此时,就做到内存的自动回收,避免内存泄漏的问题。这是智能指针整体的一个工作原理,在此基础上,通过对资源的管理方式,产生出了四种不同类型的智能指针。

二、智能指针特性分析

为了方便理解,只列出部分源码进行分析,减少干扰。此源码取自于vs2022,观察vs2015,源码逻辑相似,设计上有所不同。

1.std::shared_ptr

特性

  • 共享所有权:允许多个shared_ptr共享同一个对象,通过引用计数跟踪对象的生命周期。
  • 引用计数:每次创建或复制一个shared_ptr,引用计数增加;当引用计数为0时,资源被释放。
  • 用途:适合在多个地方共享相同资源的场景,如共享缓存、组件间数据传递等。

示例

c 复制代码
#include <iostream>
#include  <memory> // 共享指针必须要包含的头文件
using namespace std;
int main()
{
	// 最好使用make_shared创建共享指针,
	shared_ptr<int> p1 = make_shared<int>(5);//make_shared 创建空对象,
	*p1 = 10;
	cout << "p1 = " << *p1 << endl; // 输出10

	// 打印引用个数:1
	cout << "p1 count = " << p1.use_count() << endl;

	// 第2个 shared_ptr 对象指向同一个指针
	std::shared_ptr<int> p2(p1);

	// 输出2
	cout << "p2 count = " << p2.use_count() << endl;
	cout << "p1 count = " << p1.use_count() << endl;

	// 比较智能指针,p1 等于 p2
	if (p1 == p2) {
		std::cout << "p1 and p2 are pointing to same pointer\n";
	}

	p1.reset();// 无参数调用reset,无关联指针,引用个数为0
	cout << "p1 Count = " << p1.use_count() << endl;

	p1.reset(new int(11));// 带参数调用reset,引用个数为1
	cout << "p1 Count = " << p1.use_count() << endl;

	p1 = nullptr;// 把对象重置为NULL,引用计数为0
	cout << "p1  Reference Count = " << p1.use_count() << endl;
	if (!p1) {
		cout << "p1 is NULL" << endl; // 输出
	}
	return 0;
}

源码

cpp 复制代码
_EXPORT_STD template <class _Ty>
class shared_ptr : public _Ptr_base<_Ty> { // class for reference counted resource management
private:
    using _Mybase = _Ptr_base<_Ty>;

public:
    using typename _Mybase::element_type;
 					......
				   ~ ~ ~ ~ 
				   ......

template <class _Ux,
    enable_if_t<conjunction_v<conditional_t<is_array_v<_Ty>, _Can_array_delete<_Ux>, _Can_scalar_delete<_Ux>>,
                    _SP_convertible<_Ux, _Ty>>,
        int> = 0>
explicit shared_ptr(_Ux* _Px) { // construct shared_ptr object that owns _Px
    if constexpr (is_array_v<_Ty>) {
        _Setpd(_Px, default_delete<_Ux[]>{});
    } else {
        _Temporary_owner<_Ux> _Owner(_Px);
        _Set_ptr_rep_and_enable_shared(_Owner._Ptr, new _Ref_count<_Ux>(_Owner._Ptr));
        _Owner._Ptr = nullptr;
    }
}
					......
				   ~ ~ ~ ~ 
				   ......
    shared_ptr(const shared_ptr& _Other) noexcept { // construct shared_ptr object that owns same resource as _Other
        this->_Copy_construct_from(_Other);
    }
    			......
				   ~ ~ ~ ~ 
				   ......

    ~shared_ptr() noexcept { // release resource
        this->_Decref();
    }

    shared_ptr& operator=(const shared_ptr& _Right) noexcept {
        shared_ptr(_Right).swap(*this);
        return *this;
    }
					......
				   ~ ~ ~ ~ 
				   ......
    void swap(shared_ptr& _Other) noexcept {
        this->_Swap(_Other);
    }

    void reset() noexcept { // release resource and convert to empty shared_ptr object
        shared_ptr().swap(*this);
    }
   					......
				   ~ ~ ~ ~ 
				   ......
    using _Mybase::get;

    template <class _Ty2 = _Ty, enable_if_t<!disjunction_v<is_array<_Ty2>, is_void<_Ty2>>, int> = 0>
    _NODISCARD _Ty2& operator*() const noexcept {
        return *get();
    }
   					......
				   ~ ~ ~ ~ 
				   ......
 template <class _Ux>
 void _Set_ptr_rep_and_enable_shared(_Ux* const _Px, _Ref_count_base* const _Rx) noexcept { // take ownership of _Px
     this->_Ptr = _Px;
     this->_Rep = _Rx;
     if constexpr (conjunction_v<negation<is_array<_Ty>>, negation<is_volatile<_Ux>>, _Can_enable_shared<_Ux>>) {
         if (_Px && _Px->_Wptr.expired()) {
             _Px->_Wptr = shared_ptr<remove_cv_t<_Ux>>(*this, const_cast<remove_cv_t<_Ux>*>(_Px));
         }
     }
 }
};

template <class _Ty>
class _Ptr_base { // base class for shared_ptr and weak_ptr
public:
    using element_type = remove_extent_t<_Ty>;

    _NODISCARD long use_count() const noexcept {
        return _Rep ? _Rep->_Use_count() : 0;
    }
				   ......
				   ~ ~ ~ ~ 
				   ......

    _Ptr_base(const _Ptr_base&)            = delete;
    _Ptr_base& operator=(const _Ptr_base&) = delete;

protected:
    _NODISCARD element_type* get() const noexcept {
        return _Ptr;
    }

    constexpr _Ptr_base() noexcept = default;

    ~_Ptr_base() = default;

   				......
			   ~ ~ ~ ~ 
			   ......

    template <class _Ty2>
    void _Copy_construct_from(const shared_ptr<_Ty2>& _Other) noexcept {
        // implement shared_ptr's (converting) copy ctor
        _Other._Incref();

        _Ptr = _Other._Ptr;
        _Rep = _Other._Rep;
    }
    void _Incref() const noexcept {
        if (_Rep) {
            _Rep->_Incref();
        }
    }

    void _Decref() noexcept { // decrement reference count
        if (_Rep) {
            _Rep->_Decref();
        }
    }

    void _Swap(_Ptr_base& _Right) noexcept { // swap pointers
        _STD swap(_Ptr, _Right._Ptr);
        _STD swap(_Rep, _Right._Rep);
    }
				......
			   ~ ~ ~ ~ 
			   ......
   
    void _Incwref() const noexcept {
        if (_Rep) {
            _Rep->_Incwref();
        }
    }

    void _Decwref() noexcept { // decrement weak reference count
        if (_Rep) {
            _Rep->_Decwref();
        }
    }

private:
    element_type* _Ptr{nullptr};
    _Ref_count_base* _Rep{nullptr};
				    ......
				   ~ ~ ~ ~ 
				   ......
};

通过观察类shared_ptr可知,它继承于_Ptr_base 。

在_Ptr_base类中,有两个成员变量:

1._Ptr,存放裸指针;

2._Ref_count_base记录使用计数等操作。

当shared_ptr指针进行拷贝构造时,在内部会调用"this->_Copy_construct_from(_Other) ",此成员函数来自于父类。观察_Copy_construct_from内部实现," _Other._Incref(); "内部引用计数加一。同时将_Other内部的裸指针(_Ptr)以及_Ref_count_base指针赋值给现在的对象。

_Other._Incref(),查看源码,会发现它是由_Ref_count_base指针完成的。所以当_Ref_count_base指针赋值给现在的对象时,此时对象内部的引用计数已经加一。

当shared_ptr对象生命周期结束时,调用析构函数,通过观察析构函数,会发现调用了内部函数_Decref()。通过观察内部函数,引用计数的减一也是通过_Ref_count_base指针完成的。_Rep->_Decref(),在内部引用计数会减一,当引用计数为零时,会释放裸指针。

当shared_ptr指针进行赋值时,观察重载操作符"operator="。在重载操作内部,调用了拷贝构造函数shared_ptr(_Right),那么此时就回到了拷贝构造的操作逻辑,它的内部引用计数就会加一,创建了一个临时对象,将临时对象内部信息与当前对象内部信息进行交换。这样就达到了,在进行赋值时,内部引用计数加一

列举一个shared_ptr p(new int(11));的例子。

在运行时,会调用shared_ptr(_Ux* _Px)构造函数。在函数内部,new一个_Ref_count<_Ux>(_Owner._Ptr)对象,赋值给_Rep指针。当p指针超出生命周期后,调用析构函数,最后在引用计数为零时,调用 delete 将裸指针删除。


cpp 复制代码
class __declspec(novtable) _Ref_count_base { // common code for reference counting
private:
  					......
				   ~ ~ ~ ~ 
				   ......
    _Atomic_counter_t _Uses  = 1;
    _Atomic_counter_t _Weaks = 1;

 					......
				   ~ ~ ~ ~ 
				   ......
    void _Incref() noexcept { // increment use count
        _MT_INCR(_Uses);
    }

    void _Incwref() noexcept { // increment weak reference count
        _MT_INCR(_Weaks);
    }

    void _Decref() noexcept { // decrement use count
        if (_MT_DECR(_Uses) == 0) {
            _Destroy();
            _Decwref();
        }
    }

    void _Decwref() noexcept { // decrement weak reference count
        if (_MT_DECR(_Weaks) == 0) {
            _Delete_this();
        }
    }

    long _Use_count() const noexcept {
        return static_cast<long>(_Uses);
    }
     			   ......
				   ~ ~ ~ ~ 
				   ......
};

template <class _Ty>
class _Ref_count : public _Ref_count_base { // handle reference counting for pointer without deleter
public:
    explicit _Ref_count(_Ty* _Px) : _Ref_count_base(), _Ptr(_Px) {}

private:
    void _Destroy() noexcept override { // destroy managed resource
        delete _Ptr;
    }

    void _Delete_this() noexcept override { // destroy self
        delete this;
    }

    _Ty* _Ptr;
};

通过构造和析构函数分析可知,引用计数的增加和减少以及裸指针的释放,都是有_Ref_count_base指针来操作的。通过观察源码,成员变量:1._Uses,用来记录强引用计数;2._Weaks 代表弱引用计数,即有多少个 std::weak_ptr 对象指向该资源。_Incref()强引用计数加一操作,_Decref()强引用计数减一的操作,当引用计数为零时,对裸指针进行释放的操作,同时对弱引用计数_Weaks进行减一操作。_Weaks 代表弱引用计数,此引用计数,它不会影响资源的生命周期,但允许访问对象。

弱引用计数(_Weaks)

_Weaks 代表弱引用计数,即有多少个 std::weak_ptr 对象指向该资源。

std::weak_ptr 是一种弱引用,它不会影响资源的生命周期,但允许访问对象。换句话说,weak_ptr 不增加 _Uses 的强引用计数,而是只增加 _Weaks 的弱引用计数。

当 _Uses(强引用计数)为 0 时,对象会被销毁,但 _Weaks(弱引用计数)为 0 时,才会真正释放控制块(即 std::shared_ptr 和 std::weak_ptr 用来管理对象的元数据,包括计数器等)。

_Weaks = 1 的含义:

当 _Atomic_counter_t _Weaks = 1,意味着即使没有 std::weak_ptr 对象,_Weaks 也至少会有一个引用存在,这个引用是控制块的自我引用。控制块通常包含引用计数器等元数据,它自身需要至少一个弱引用来跟踪。当最后一个 std::weak_ptr 被销毁时,弱引用计数 _Weaks 变为 0,控制块(以及所有元数据)才会被释放。

对象管理:

当 _Uses 变为 0 时,实际的对象会被释放(即对象的析构函数被调用),但控制块并不会立即释放,直到 _Weaks 也变为 0。

弱引用计数 _Weaks 的初始值为 1,即便没有 std::weak_ptr 实例,因为控制块必须维持一个弱引用,以确保即使所有 std::shared_ptr 对象销毁后,仍然可以通过 weak_ptr 来访问控制块的状态。


2.std::unique_ptr

特性

  • 特点:唯一拥有资源的智能指针,同一时间只能有一个 unique_ptr 指向某个对象。不能复制,但可以移动。
  • 适用场景:适合需要明确生命周期管理的场景,比如某个对象只在函数内有效。
    示例
c 复制代码
td::unique_ptr<int> p1(new int(10));  // 创建并指向int类型的智能指针
std::unique_ptr<int> p2 = std::move(p1);  // 移动p1的所有权给p2// p1此时为nullptr,p2拥有对象

源码

cpp 复制代码
template <class _Ty2, class _Dx2,
        enable_if_t<conjunction_v<negation<is_array<_Ty2>>, is_assignable<_Dx&, _Dx2>,
                        is_convertible<typename unique_ptr<_Ty2, _Dx2>::pointer, pointer>>,
            int> = 0>
    _CONSTEXPR23 unique_ptr& operator=(unique_ptr<_Ty2, _Dx2>&& _Right) noexcept {
        reset(_Right.release());
        _Mypair._Get_first() = _STD forward<_Dx2>(_Right._Mypair._Get_first());
        return *this;
    }

    template <class _Dx2 = _Dx, enable_if_t<is_move_assignable_v<_Dx2>, int> = 0>
    _CONSTEXPR23 unique_ptr& operator=(unique_ptr&& _Right) noexcept {
        reset(_Right.release());
        _Mypair._Get_first() = _STD forward<_Dx>(_Right._Mypair._Get_first());
        return *this;
    }
    
    _CONSTEXPR23 ~unique_ptr() noexcept {
	    if (_Mypair._Myval2) {
	        _Mypair._Get_first()(_Mypair._Myval2);
	    }
	}

通过观察移动赋值运算符函数可知,参数为右值引用。在传参赋值时,通过显式的 std::move 转移所有权,使得所有权转移的操作更加明确,防止意外的资源转移。这种显式的操作降低了误用的可能性。在函数内部,_Right.release()_Right 将释放所管理的指针,同时将所管理的指针返回出来。同时返回的参数,通过reset()函数,将指针设置到此unique_ptr指针中。从而达到资源移动的效果。

当类对象超过生命周期时,调用析构函数。观察源码,_Mypair._Myval2存放裸指针,当指针存在时,_Mypair._Get_first()返回删除对象,对_Mypair._Myval2指针进行释放。它的删除操作可参考default_delete的 operator()函数,在内部对数据进行了delete操作。(关于内存释放逻辑 "_Mypair._Get_first()(_Mypair._Myval2)",在下面进行解释)


cpp 复制代码
_EXPORT_STD template <class _Ty, class _Dx /* = default_delete<_Ty> */>
class unique_ptr { // non-copyable pointer to an object
public:
				    ......
				   ~ ~ ~ ~ 
				   ......
    _CONSTEXPR23 ~unique_ptr() noexcept {
        if (_Mypair._Myval2) {
            _Mypair._Get_first()(_Mypair._Myval2);
        }
    }
    unique_ptr(const unique_ptr&)            = delete;
    unique_ptr& operator=(const unique_ptr&) = delete;

private:
    template <class, class>
    friend class unique_ptr;

    _Compressed_pair<_Dx, pointer> _Mypair;
};

template <class _Ty1, class _Ty2, bool = is_empty_v<_Ty1> && !is_final_v<_Ty1>>
class _Compressed_pair final : private _Ty1 { // store a pair of values, deriving from empty first
public:
    _Ty2 _Myval2;

    using _Mybase = _Ty1; // for visualization

   ......
   ~ ~ ~ ~ 
   ......

    constexpr _Ty1& _Get_first() noexcept {
        return *this;
    }
    
    constexpr const _Ty1& _Get_first() const noexcept {
        return *this;
    }
};
	
_EXPORT_STD template <class _Ty>
struct default_delete { // default deleter for unique_ptr
    constexpr default_delete() noexcept = default;

    template <class _Ty2, enable_if_t<is_convertible_v<_Ty2*, _Ty*>, int> = 0>
    _CONSTEXPR23 default_delete(const default_delete<_Ty2>&) noexcept {}

    _CONSTEXPR23 void operator()(_Ty* _Ptr) const noexcept /* strengthened */ { // delete a pointer
        static_assert(0 < sizeof(_Ty), "can't delete an incomplete type");
        delete _Ptr;
    }
};

观察unique_ptr的类模板可知,default_delete 类作为默认参数,传入到模板中。同时它又作为类_Compressed_pair的模板参数。观察类模板_Compressed_pair可知,它私有继承了_Ty1。那么此时成员函数_Get_first()返回 this,即为default_delete 对象。回顾到unique_ptr 的析构函数,_Mypair._Get_first()返回default_delete对象。又因为结构体default_delete,重载了"()"运算符,并在内部进行了"delete _Ptr"操作。所以"_Mypair._Get_first()(_Mypair._Myval2);*"就对_Mypair._Myval2对象进行了释放。


3.std::weak_ptr

特性

-特点:它不控制对象的生命周期,而是观察 shared_ptr 管理的对象。避免循环引用导致的内存泄漏。

适用场景:常用于防止 shared_ptr 循环引用,比如在双向关联的类中。

示例

c 复制代码
#include <iostream>
#include <memory>
using namespace std;

int main()
{

    std::shared_ptr<int> sp1(new int(10));
    std::shared_ptr<int> sp2(sp1);
    std::weak_ptr<int> wp(sp2);//wp会增加工作数量
    //输出和 wp 同指向的 shared_ptr 类型指针的数量
    cout << wp.use_count() << endl;
    //释放 sp2
    sp2.reset();
    cout << wp.use_count() << endl;
    //借助 lock() 函数,返回一个和 wp 同指向的 shared_ptr 类型指针,获取其存储的数据
    cout << *(wp.lock()) << endl;
    return 0;
}

源码

cpp 复制代码
_EXPORT_STD template <class _Ty>
class weak_ptr : public _Ptr_base<_Ty> { // class for pointer to reference counted resource
public:
			 	......
			   ~ ~ ~ ~ 
			   ......
    weak_ptr(const weak_ptr& _Other) noexcept {
        this->_Weakly_construct_from(_Other); // same type, no conversion
    }

    template <class _Ty2, enable_if_t<_SP_pointer_compatible<_Ty2, _Ty>::value, int> = 0>
    weak_ptr(const shared_ptr<_Ty2>& _Other) noexcept {
        this->_Weakly_construct_from(_Other); // shared_ptr keeps resource alive during conversion
    }
    			......
			   ~ ~ ~ ~ 
			   ......

    ~weak_ptr() noexcept {
        this->_Decwref();
    }

    weak_ptr& operator=(const weak_ptr& _Right) noexcept {
        weak_ptr(_Right).swap(*this);
        return *this;
    }

    void reset() noexcept { // release resource, convert to null weak_ptr object
        weak_ptr{}.swap(*this);
    }

    void swap(weak_ptr& _Other) noexcept {
        this->_Swap(_Other);
    }

  				~ ~ ~ ~ 
    _NODISCARD shared_ptr<_Ty> lock() const noexcept { // convert to shared_ptr
        shared_ptr<_Ty> _Ret;
        (void) _Ret._Construct_from_weak(*this);
        return _Ret;
    }
};

template <class _Ty>
class _Ptr_base { // base class for shared_ptr and weak_ptr
public:
    				 ~ ~ ~ ~ 
    template <class _Ty2>
    void _Weakly_construct_from(const _Ptr_base<_Ty2>& _Other) noexcept { // implement weak_ptr's ctors
        if (_Other._Rep) {
            _Ptr = _Other._Ptr;
            _Rep = _Other._Rep;
            _Rep->_Incwref();
        } else {
            _STL_INTERNAL_CHECK(!_Ptr && !_Rep);
        }
    }
    
    void _Incwref() const noexcept {
        if (_Rep) {
            _Rep->_Incwref();
        }
    }

    void _Decwref() noexcept { // decrement weak reference count
        if (_Rep) {
            _Rep->_Decwref();
        }
    }
private:
    element_type* _Ptr{nullptr};
    _Ref_count_base* _Rep{nullptr};
    			......
			   ~ ~ ~ ~ 
			   ......
};

观察类weak_ptr可知,它继承了类_Ptr_base。并且对数据的操作很多是_Ptr_base提供的成员方法。所以weak_ptr指针的内部调用和shared_ptr的调用方式类似。

当传入shared_ptr指针,进行weak_ptr对象构造时,查看源码,内部会调用 this->_Weakly_construct_from(_Other);此函数为_Ptr_base的成员函数。在此函数内部,将_Other内部的成员变量(shared_ptr指针分析时已经说明)赋值给当前类对象。同时调用_Rep->_Incwref(),弱引用计数加一。

当weak_ptr对象生命周期结束时,调用析构函数,在析构函数中会调用 this->_Decwref()。查看_Ptr_base的成员函数_Decwref()。在函数内部会调用_MT_DECR(_Weaks),弱引用计数减一。若此时若引用计数为零时,调用_Delete_this()成员函数。在函数内部会销毁当前weak_ptr对象。


cpp 复制代码
_EXPORT_STD template <class _Ty>
class weak_ptr : public _Ptr_base<_Ty> { // class for pointer to reference counted resource
public:
			 	......
			   ~ ~ ~ ~ 
			   ......
    _NODISCARD shared_ptr<_Ty> lock() const noexcept { // convert to shared_ptr
        shared_ptr<_Ty> _Ret;
        (void) _Ret._Construct_from_weak(*this);
        return _Ret;
    }
};

template <class _Ty2>
bool _Construct_from_weak(const weak_ptr<_Ty2>& _Other) noexcept {
    // implement shared_ptr's ctor from weak_ptr, and weak_ptr::lock()
    if (_Other._Rep && _Other._Rep->_Incref_nz()) {
        _Ptr = _Other._Ptr;
        _Rep = _Other._Rep;
        return true;
    }

    return false;
}

当调用成员函数 weak_ptr::lock()时,在函数内部会创建一个临时对象,调用shared_ptr::_Construct_from_weak,并将 weak_ptr内部的裸指针以及_Ref_count_base指针,赋值给临时对象。当时weak_ptr,没有管理指针时,返回false。同时lock()将临时对象返回。所以外部通过判断返回值是否为空,来决定是否管理shared_ptr指针。


4.auto_ptr

特性

因为std::unique_ptr是auto_ptr的替代品,所以他们的设计相似,这里不再分析auto_ptr的源码。以下主要列举容易出现的问题,以及遗弃的原因。

被遗弃的原因:

在C++中,auto_ptr是最初引入的智能指针之一,它的目的是为了自动管理动态分配的内存,以避免内存泄漏。然而,随着C++11的到来,auto_ptr被废弃并由unique_ptr取代。主要原因在于auto_ptr的设计存在一些缺陷,这些缺陷使得它在实际使用中可能导致不安全的内存操作。

auto_ptr的主要问题:

  1. 所有权转移:auto_ptr在赋值操作时会发生所有权的转移。当一个auto_ptr对象被另一个auto_ptr对象赋值后,原对象会失去对内存的所有权,这会导致原对象变成空指针,从而可能引发悬空指针的问题。
  2. 多次析构:如果两个auto_ptr对象指向同一个内存地址,当它们的生命周期结束时,会尝试对同一内存地址进行多次析构,这会导致程序崩溃。
  3. 与容器不兼容:auto_ptr不能安全地存储在标准容器中,因为容器的元素可能会被复制或赋值,而auto_ptr的复制或赋值行为会导致原有对象失去对内存的控制。

示例

cpp 复制代码
 //多次析构:如果两个auto_ptr对象指向同一个内存地址,当它们的生命周期结束时,
        //会尝试对同一内存地址进行多次析构,这会导致程序崩溃。
        {
                Test* temptest = new Test;
                auto_ptr<Test> test1(temptest);
                auto_ptr<Test> test2(temptest);
        }
        
        //所有权转移:auto_ptr在赋值操作时会发生所有权的转移。
        // 当一个auto_ptr对象被另一个auto_ptr对象赋值后,
        //原对象会失去对内存的所有权,这会导致原对象变成空指针,从而可能引发悬空指针的问题。
        {
                auto_ptr<Test> t1(new Test);
                auto_ptr<Test> t2(new Test);
                t1 = t2;
        }

        //与容器不兼容:auto_ptr不能安全地存储在标准容器中,因为容器的元素可能会被复制或赋值,
        //而auto_ptr的复制或赋值行为会导致原有对象失去对内存的控制。
        {
                vector<auto_ptr<string>> vec;
                auto_ptr<string> p3(new string("I'm P3"));
                auto_ptr<string> p4(new string("I'm P4"));

                vec.push_back(std::move(p3));
                vec.push_back(std::move(p4));

                cout << "vec.at(0):" << *vec.at(0) << endl;
                cout << "vec[1]:" << *vec[1] << endl;


                // 风险来了:
                vec[0] = vec[1];
                cout << "vec.at(0):" << *vec.at(0) << endl;
                cout << "vec[1]:" << *vec[1] << endl;
        }

总结

`智能指针总是八股文的一部分,与其记住它的特性,不如知道它的工作逻辑,推导出所以然。记性总是很差,为何不去理解它的原理,感受它的趣味。

相关推荐
烦躁的大鼻嘎7 分钟前
模拟算法实例讲解:从理论到实践的编程之旅
数据结构·c++·算法·leetcode
IU宝11 分钟前
C/C++内存管理
java·c语言·c++
湫ccc11 分钟前
《Python基础》之pip换国内镜像源
开发语言·python·pip
fhvyxyci12 分钟前
【C++之STL】摸清 string 的模拟实现(下)
开发语言·c++·string
qq_4597300314 分钟前
C 语言面向对象
c语言·开发语言
菜鸟学Python23 分钟前
Python 数据分析核心库大全!
开发语言·python·数据挖掘·数据分析
C++忠实粉丝24 分钟前
计算机网络socket编程(4)_TCP socket API 详解
网络·数据结构·c++·网络协议·tcp/ip·计算机网络·算法
一个小坑货31 分钟前
Cargo Rust 的包管理器
开发语言·后端·rust
bluebonnet2735 分钟前
【Rust练习】22.HashMap
开发语言·后端·rust
古月居GYH35 分钟前
在C++上实现反射用法
java·开发语言·c++