【C++ | 內存管理】C++ 智能指針 std::shared_ptr 詳解及使用例子代碼

😁博客主頁😁:🚀https://blog.csdn.net/wkd_007🚀
🤑博客內容🤑:🍭嵌入式開發、Linux、C語言、C++、數據結構、音視頻🍭
🤣本文內容🤣:🍭介紹 C++ 智能指針🍭
😎金句分享😎:🍭你不能選擇最好的,但最好的會來選擇你——泰戈爾🍭
?發布時間?:

本文未經允許,不得轉發!!!

目錄

  • 🎄一、概述
  • 🎄二、std::shared_ptr 的使用
    • ?2.1 std::shared_ptr 構造、析構——創建智能指針
    • ?2.2 std::shared_ptr 賦值運算符(=)
    • ?2.3 std::shared_ptr 指針的訪問(get、->、*、bool、use_count)
    • ?2.4 std::shared_ptr 獲取刪除器
    • ?2.5 std::shared_ptr 指針的重置
    • ?2.6 std::shared_ptr 指針交換
  • 🎄三、std::shared_ptr 的實現原理
    • ?3.1 控制塊(Control Block)
    • ?3.2 內存布局
    • ?3.3 引用計數操作
    • ?3.4 自定義刪除器
  • 🎄四、使用的幾個注意點
    • ?4.1 不要將棧地址傳遞給 std::shared_ptr指針
    • ?4.2 std::shared_ptr 指針作為函數參數和返回值
    • ?4.3 優先使用make_shared
    • ?4.4 避免重復接管原始指針
    • ?4.5 避免循環引用
  • 🎄五、總結


在這里插入圖片描述

在這里插入圖片描述

🎄一、概述

在C++11編程中,智能指針已經成為管理動態內存的標配工具。最開始的智能指針有 std::auto_ptr,后面 C++11 標準出來后,std::auto_ptr就廢棄了,新增了三個智能指針:std::unique_ptrstd::shared_ptrstd::weak_ptr

類型特點
std::unique_ptr獨占所有權,不可拷貝,只能移動;性能接近裸指針,無額外開銷
std::shared_ptr共享所有權,通過引用計數管理內存;支持拷貝,適合多個對象共享同一資源
std::weak_ptr弱引用,不增加引用計數;用于解決 shared_ptr 的循環引用問題

智能指針 是 C++ 中用于自動化管理動態內存的工具。它們通過封裝 裸指針(raw pointer) 并利用 RAII(資源獲取即初始化)機制,實現了內存的自動釋放,從而幫助開發者避免內存泄漏、懸垂指針等常見內存管理問題。
在 C++11 及之后的代碼中,智能指針應成為動態內存管理的默認選擇,僅在需要極致性能或與遺留代碼交互時使用裸指針。

std::shared_ptr 是 C++11 引入的智能指針,用于管理動態分配對象的生命周期。它通過 引用計數(Reference Counting) 機制,允許多個 shared_ptr 實例共享同一對象的所有權。當最后一個持有對象的 shared_ptr 被銷毀時,對象會被自動釋放,從而避免內存泄漏。

核心特性

  • 共享所有權:多個 shared_ptr 可指向同一對象。

  • 自動釋放:引用計數歸零時自動銷毀對象并釋放內存。

  • 線程安全:引用計數的增減是原子的(但對象訪問需手動同步)。

  • 支持自定義刪除器:允許指定對象銷毀時的自定義邏輯。


在這里插入圖片描述

🎄二、std::shared_ptr 的使用

std::shared_ptr 定義在<memory>頭文件中,這個小節主要介紹其成員函數及其用法:

?2.1 std::shared_ptr 構造、析構——創建智能指針

std::shared_ptr 的構造函數的函數原型如下:

// 1.默認
constexpr shared_ptr() noexcept;// 2.空指針
constexpr shared_ptr(nullptr_t) : shared_ptr() {}// 3.使用另一個指針進行初始化
template <class U> explicit shared_ptr (U* p);// 4.使用刪除器進行初始化
template <class U, class D> shared_ptr (U* p, D del);
template <class D> shared_ptr (nullptr_t p, D del);// 5.使用分配器進行初始化
template <class U, class D, class Alloc> shared_ptr (U* p, D del, Alloc alloc);
template <class D, class Alloc> shared_ptr (nullptr_t p, D del, Alloc alloc);// 6.拷貝構造
template <class U> shared_ptr (const shared_ptr<U>& x) noexcept;
shared_ptr(const shared_ptr&) noexcept = default;// 7.從 weak_ptr 拷貝
template <class U> explicit shared_ptr (const weak_ptr<U>& x);// 8.移動構造函數
shared_ptr (shared_ptr&& x) noexcept;
template <class U> shared_ptr (shared_ptr<U>&& x) noexcept;// 9.從其他類型移動
template <class U> shared_ptr (auto_ptr<U>&& x);
template <class U, class D> shared_ptr (unique_ptr<U,D>&& x);// 10.
template <class U> shared_ptr (const shared_ptr<U>& x, element_type* p) noexcept;

std::shared_ptr 構造函數:

  • 默認構造:創建空 shared_ptr shared_ptr<int> p;
  • 從指針構造:接管原始指針所有權 shared_ptr<int> p(new int(42));
  • 拷貝構造:共享所有權,引用計數+1 shared_ptr<int> p2(p1);
  • 移動構造:轉移所有權,原指針置空 shared_ptr<int> p3(std::move(p2));
  • 帶刪除器的構造:指定自定義銷毀邏輯 shared_ptr<FILE> fp(fopen("a.txt", "r"), fclose);

std::shared_ptr 析構函數: 減少引用計數,若計數歸零,調用刪除器釋放資源。

🌰舉例子,shared_ptr_constructor.cpp

// shared_ptr constructor example
#include <iostream>
#include <memory>struct C {int* data;};int main () {std::shared_ptr<int> p1;std::shared_ptr<int> p2 (nullptr);std::shared_ptr<int> p3 (new int(3));std::shared_ptr<int> p4 (new int, std::default_delete<int>());std::shared_ptr<int> p5 (new int, [](int* p){delete p;}, std::allocator<int>());std::shared_ptr<int> p6 (p5);std::shared_ptr<int> p7 (std::move(p6));std::shared_ptr<int> p8 (std::unique_ptr<int>(new int));std::shared_ptr<C> obj (new C);std::shared_ptr<int> p9 (obj, obj->data);std::cout << "use_count:\n";std::cout << "p1: " << p1.use_count() << '\n';std::cout << "p2: " << p2.use_count() << '\n';std::cout << "p3: " << p3.use_count() << '\n';std::cout << "p4: " << p4.use_count() << '\n';std::cout << "p5: " << p5.use_count() << '\n';std::cout << "p6: " << p6.use_count() << '\n';std::cout << "p7: " << p7.use_count() << '\n';std::cout << "p8: " << p8.use_count() << '\n';std::cout << "p9: " << p9.use_count() << '\n';return 0;
}

運行結果:
在這里插入圖片描述


?2.2 std::shared_ptr 賦值運算符(=)

上篇文章 C++ 智能指針 std::unique_ptr 詳解 介紹過,unique_ptr模板類禁用了 拷貝賦值運算符,只保留了 移動賦值運算符。而 shared_ptr 模板類是支持共享的,會議不同的 拷貝賦值運算符移動賦值運算符。下面來看看怎么使用。

👉如果對 移動賦值運算符 不清楚的,可以參考這篇文章:一文了解C++11的 移動賦值運算符 。

函數原型:

// 1. 拷貝賦值:共享所有權
shared_ptr& operator= (const shared_ptr& x) noexcept;
template <class U> shared_ptr& operator= (const shared_ptr<U>& x) noexcept;// 2. 移動賦值
shared_ptr& operator= (shared_ptr&& x) noexcept;
template <class U> shared_ptr& operator= (shared_ptr<U>&& x) noexcept;// 3.其他類型指針的移動賦值
template <class U> shared_ptr& operator= (auto_ptr<U>&& x);
template <class U, class D> shared_ptr& operator= (unique_ptr<U,D>&& x);

🌰舉例子:

// shared_ptr::operator= example
#include <iostream>
#include <memory>int main () {std::shared_ptr<int> foo;std::shared_ptr<int> bar (new int(10));foo = bar;                          // copybar = std::make_shared<int> (20);   // movestd::unique_ptr<int> unique (new int(30));foo = std::move(unique);            // move from unique_ptrstd::cout << "*foo: " << *foo << '\n';std::cout << "*bar: " << *bar << '\n';return 0;
}

運行結果:
在這里插入圖片描述


?2.3 std::shared_ptr 指針的訪問(get、->、*、bool、use_count)

函數原型:

element_type* get() const noexcept;
element_type* operator->() const noexcept;	// 返回的值和 get() 一樣
element_type& operator*() const noexcept;	// 等價于: *get().
explicit operator bool() const noexcept;
long int use_count() const noexcept;		// 獲取引用計數
bool unique() const noexcept;				// use_count()==1 時為true

🌰舉例子:

// shared_ptr_Access.cpp
#include <iostream>
#include <memory>struct C { int a; int b; };int main () {// 1.getint* p = new int (10);std::shared_ptr<int> a (p);if (a.get()==p)std::cout << "a and p point to the same location\n";// three ways of accessing the same address:std::cout << *a.get() << "\n";std::cout << *a << "\n";std::cout << *p << "\n\n";//----------------------------------------------------------------// 2.->std::shared_ptr<C> foo;std::shared_ptr<C> bar (new C);foo = bar;foo->a = 10;bar->b = 20;if (foo) std::cout << "foo: " << foo->a << ' ' << foo->b << '\n';if (bar) std::cout << "bar: " << bar->a << ' ' << bar->b << "\n\n";// 3. *std::shared_ptr<int> foo_3 (new int);std::shared_ptr<int> bar_3 (new int (100));*foo_3 = *bar_3 * 2;std::cout << "foo_3: " << *foo_3 << '\n';std::cout << "bar_3: " << *bar_3 << "\n\n";// 4. boolstd::cout << "foo_3: " << (foo_3?"not null":"null") << "\n\n";// 5.use_countstd::shared_ptr<int> foo_5 (new int);std::shared_ptr<int> bar_5 (foo_5);std::cout << "foo_5 count: " << foo_5.use_count() << ", bar_5 count: " << bar_5.use_count() << "\n\n";// 6.std::shared_ptr<int[]> u_Access4(new int[10]{0,1,2,3,4,5,6,7,8,9});std::cout << "u_Access4: [";for(int i=0; i<10; i++)std::cout << u_Access4[i] << ", ";std::cout << "]" << std::endl;return 0;
}

運行結果:
在這里插入圖片描述


?2.4 std::shared_ptr 獲取刪除器

獲取刪除器的功能使用的比較少,需要學習的直接看例子。

函數原型:

template <class D, class T>  D* get_deleter (const shared_ptr<T>& sp) noexcept;

🌰舉例子:

// get_deleter example
#include <iostream>
#include <memory>struct D {    // a verbose array deleter:void operator()(int* p) {std::cout << "[deleter called]\n";delete[] p;}
};int main () {std::shared_ptr<int> foo (new int[10],D());int * bar = new int[20];// use foo's deleter to delete bar (which is unmanaged):(*std::get_deleter<D>(foo))(bar);return 0;// foo's deleter called automatically
}

運行結果:
在這里插入圖片描述


?2.5 std::shared_ptr 指針的重置

reset 函數會替換被管理的對象.

函數原型:

void reset() noexcept;
template <class U> void reset (U* p);
template <class U, class D> void reset (U* p, D del);
template <class U, class D, class Alloc> void reset (U* p, D del, Alloc alloc);

🌰舉例子:

// shared_ptr::reset example
#include <iostream>
#include <memory>int main () {std::shared_ptr<int> sp;  // emptysp.reset (new int);       // takes ownership of pointer*sp=10;std::cout << *sp << '\n';sp.reset (new int);       // deletes managed object, acquires new pointer*sp=20;std::cout << *sp << '\n';sp.reset();               // deletes managed objectreturn 0;
}

運行結果:
在這里插入圖片描述


?2.6 std::shared_ptr 指針交換

函數原型:

void swap (shared_ptr& x) noexcept;

功能:與另一個 shared_ptr 指針交換。

🌰舉例子:

// shared_ptr_Swap.cpp
#include <iostream>
#include <memory>int main()
{std::shared_ptr<int> u_Swap1(new int(1));std::cout << "u_Swap1=" << u_Swap1.get() << ", value=" << *(u_Swap1.get()) << std::endl;std::shared_ptr<int> u_Swap2(new int(2));std::cout << "u_Swap2=" << u_Swap2.get() << ", value=" << *(u_Swap2.get()) << std::endl;u_Swap1.swap(u_Swap2);std::cout << "after swap:" << std::endl;std::cout << "u_Swap1=" << u_Swap1.get() << ", value=" << *(u_Swap1.get()) << std::endl;std::cout << "u_Swap2=" << u_Swap2.get() << ", value=" << *(u_Swap2.get()) << std::endl;return 0;
}

運行結果:
在這里插入圖片描述


在這里插入圖片描述

🎄三、std::shared_ptr 的實現原理

這里先看看 std::shared_ptr 的實現代碼。

下面代碼是Ubuntu18.04系統截取的,路徑是:/usr/include/c++/7/bits/shared_ptr.h,是 std::shared_ptr 的實現代碼,從代碼看,繼承自 __shared_ptr

template<typename _Tp>
class shared_ptr : public __shared_ptr<_Tp>
{template<typename... _Args>using _Constructible = typename enable_if<is_constructible<__shared_ptr<_Tp>, _Args...>::value>::type;template<typename _Arg>using _Assignable = typename enable_if<is_assignable<__shared_ptr<_Tp>&, _Arg>::value, shared_ptr&>::type;public:using element_type = typename __shared_ptr<_Tp>::element_type;#if __cplusplus > 201402L# define __cpp_lib_shared_ptr_weak_type 201606using weak_type = weak_ptr<_Tp>;
#endif/***  @brief  Construct an empty %shared_ptr.*  @post   use_count()==0 && get()==0*/constexpr shared_ptr() noexcept : __shared_ptr<_Tp>() { }shared_ptr(const shared_ptr&) noexcept = default;/***  @brief  Construct a %shared_ptr that owns the pointer @a __p.*  @param  __p  A pointer that is convertible to element_type*.*  @post   use_count() == 1 && get() == __p*  @throw  std::bad_alloc, in which case @c delete @a __p is called.*/template<typename _Yp, typename = _Constructible<_Yp*>>explicit shared_ptr(_Yp* __p) : __shared_ptr<_Tp>(__p) { }/***  @brief  Construct a %shared_ptr that owns the pointer @a __p*          and the deleter @a __d.*  @param  __p  A pointer.*  @param  __d  A deleter.*  @post   use_count() == 1 && get() == __p*  @throw  std::bad_alloc, in which case @a __d(__p) is called.**  Requirements: _Deleter's copy constructor and destructor must*  not throw**  __shared_ptr will release __p by calling __d(__p)*/template<typename _Yp, typename _Deleter, typename = _Constructible<_Yp*, _Deleter>>shared_ptr(_Yp* __p, _Deleter __d) : __shared_ptr<_Tp>(__p, std::move(__d)) { }/***  @brief  Construct a %shared_ptr that owns a null pointer*          and the deleter @a __d.*  @param  __p  A null pointer constant.*  @param  __d  A deleter.*  @post   use_count() == 1 && get() == __p*  @throw  std::bad_alloc, in which case @a __d(__p) is called.**  Requirements: _Deleter's copy constructor and destructor must*  not throw**  The last owner will call __d(__p)*/template<typename _Deleter>shared_ptr(nullptr_t __p, _Deleter __d) : __shared_ptr<_Tp>(__p, std::move(__d)) { }/***  @brief  Construct a %shared_ptr that owns the pointer @a __p*          and the deleter @a __d.*  @param  __p  A pointer.*  @param  __d  A deleter.*  @param  __a  An allocator.*  @post   use_count() == 1 && get() == __p*  @throw  std::bad_alloc, in which case @a __d(__p) is called.**  Requirements: _Deleter's copy constructor and destructor must*  not throw _Alloc's copy constructor and destructor must not*  throw.**  __shared_ptr will release __p by calling __d(__p)*/template<typename _Yp, typename _Deleter, typename _Alloc,typename = _Constructible<_Yp*, _Deleter, _Alloc>>shared_ptr(_Yp* __p, _Deleter __d, _Alloc __a): __shared_ptr<_Tp>(__p, std::move(__d), std::move(__a)) { }/***  @brief  Construct a %shared_ptr that owns a null pointer*          and the deleter @a __d.*  @param  __p  A null pointer constant.*  @param  __d  A deleter.*  @param  __a  An allocator.*  @post   use_count() == 1 && get() == __p*  @throw  std::bad_alloc, in which case @a __d(__p) is called.**  Requirements: _Deleter's copy constructor and destructor must*  not throw _Alloc's copy constructor and destructor must not*  throw.**  The last owner will call __d(__p)*/template<typename _Deleter, typename _Alloc>shared_ptr(nullptr_t __p, _Deleter __d, _Alloc __a): __shared_ptr<_Tp>(__p, std::move(__d), std::move(__a)) { }// Aliasing constructor/***  @brief  Constructs a %shared_ptr instance that stores @a __p*          and shares ownership with @a __r.*  @param  __r  A %shared_ptr.*  @param  __p  A pointer that will remain valid while @a *__r is valid.*  @post   get() == __p && use_count() == __r.use_count()**  This can be used to construct a @c shared_ptr to a sub-object*  of an object managed by an existing @c shared_ptr.** @code* shared_ptr< pair<int,int> > pii(new pair<int,int>());* shared_ptr<int> pi(pii, &pii->first);* assert(pii.use_count() == 2);* @endcode*/template<typename _Yp>shared_ptr(const shared_ptr<_Yp>& __r, element_type* __p) noexcept: __shared_ptr<_Tp>(__r, __p) { }/***  @brief  If @a __r is empty, constructs an empty %shared_ptr;*          otherwise construct a %shared_ptr that shares ownership*          with @a __r.*  @param  __r  A %shared_ptr.*  @post   get() == __r.get() && use_count() == __r.use_count()*/template<typename _Yp, typename = _Constructible<const shared_ptr<_Yp>&>>shared_ptr(const shared_ptr<_Yp>& __r) noexcept: __shared_ptr<_Tp>(__r) { }/***  @brief  Move-constructs a %shared_ptr instance from @a __r.*  @param  __r  A %shared_ptr rvalue.*  @post   *this contains the old value of @a __r, @a __r is empty.*/shared_ptr(shared_ptr&& __r) noexcept : __shared_ptr<_Tp>(std::move(__r)) { }/***  @brief  Move-constructs a %shared_ptr instance from @a __r.*  @param  __r  A %shared_ptr rvalue.*  @post   *this contains the old value of @a __r, @a __r is empty.*/template<typename _Yp, typename = _Constructible<shared_ptr<_Yp>>>shared_ptr(shared_ptr<_Yp>&& __r) noexcept: __shared_ptr<_Tp>(std::move(__r)) { }/***  @brief  Constructs a %shared_ptr that shares ownership with @a __r*          and stores a copy of the pointer stored in @a __r.*  @param  __r  A weak_ptr.*  @post   use_count() == __r.use_count()*  @throw  bad_weak_ptr when __r.expired(),*          in which case the constructor has no effect.*/template<typename _Yp, typename = _Constructible<const weak_ptr<_Yp>&>>explicit shared_ptr(const weak_ptr<_Yp>& __r): __shared_ptr<_Tp>(__r) { }#if _GLIBCXX_USE_DEPRECATEDtemplate<typename _Yp, typename = _Constructible<auto_ptr<_Yp>>>shared_ptr(auto_ptr<_Yp>&& __r);
#endif// _GLIBCXX_RESOLVE_LIB_DEFECTS// 2399. shared_ptr's constructor from unique_ptr should be constrainedtemplate<typename _Yp, typename _Del, typename = _Constructible<unique_ptr<_Yp, _Del>>>shared_ptr(unique_ptr<_Yp, _Del>&& __r): __shared_ptr<_Tp>(std::move(__r)) { }#if __cplusplus <= 201402L && _GLIBCXX_USE_DEPRECATED// This non-standard constructor exists to support conversions that// were possible in C++11 and C++14 but are ill-formed in C++17.// If an exception is thrown this constructor has no effect.template<typename _Yp, typename _Del,_Constructible<unique_ptr<_Yp, _Del>, __sp_array_delete>* = 0>shared_ptr(unique_ptr<_Yp, _Del>&& __r): __shared_ptr<_Tp>(std::move(__r), __sp_array_delete()) { }
#endif/***  @brief  Construct an empty %shared_ptr.*  @post   use_count() == 0 && get() == nullptr*/constexpr shared_ptr(nullptr_t) noexcept : shared_ptr() { }shared_ptr& operator=(const shared_ptr&) noexcept = default;template<typename _Yp>_Assignable<const shared_ptr<_Yp>&>operator=(const shared_ptr<_Yp>& __r) noexcept{this->__shared_ptr<_Tp>::operator=(__r);return *this;}#if _GLIBCXX_USE_DEPRECATEDtemplate<typename _Yp>_Assignable<auto_ptr<_Yp>>operator=(auto_ptr<_Yp>&& __r){this->__shared_ptr<_Tp>::operator=(std::move(__r));return *this;}
#endifshared_ptr& operator=(shared_ptr&& __r) noexcept{this->__shared_ptr<_Tp>::operator=(std::move(__r));return *this;}template<class _Yp> _Assignable<shared_ptr<_Yp>>operator=(shared_ptr<_Yp>&& __r) noexcept{this->__shared_ptr<_Tp>::operator=(std::move(__r));return *this;}template<typename _Yp, typename _Del>_Assignable<unique_ptr<_Yp, _Del>>operator=(unique_ptr<_Yp, _Del>&& __r){this->__shared_ptr<_Tp>::operator=(std::move(__r));return *this;}private:// This constructor is non-standard, it is used by allocate_shared.template<typename _Alloc, typename... _Args>shared_ptr(_Sp_make_shared_tag __tag, const _Alloc& __a,_Args&&... __args): __shared_ptr<_Tp>(__tag, __a, std::forward<_Args>(__args)...){ }template<typename _Yp, typename _Alloc, typename... _Args>friend shared_ptr<_Yp>allocate_shared(const _Alloc& __a, _Args&&... __args);// This constructor is non-standard, it is used by weak_ptr::lock().shared_ptr(const weak_ptr<_Tp>& __r, std::nothrow_t): __shared_ptr<_Tp>(__r, std::nothrow) { }friend class weak_ptr<_Tp>;
};

?3.1 控制塊(Control Block)

每個 shared_ptr 關聯一個控制塊,包含:

  • 引用計數(use_count):跟蹤當前共享所有權的 shared_ptr 數量。

  • 弱計數(weak_count):跟蹤 weak_ptr 的數量。

  • 刪除器(Deleter):銷毀對象的邏輯(默認為 delete)。

  • 分配器(Allocator):內存分配策略(通常由 make_shared 管理)。


?3.2 內存布局

make_shared 優化:對象與控制塊一次性分配,減少內存碎片。

| Control Block (ref counts) | Managed Object |

?3.3 引用計數操作

  • 拷貝構造:遞增引用計數。

  • 析構:遞減引用計數,若歸零則調用刪除器釋放對象,若弱計數也為零則釋放控制塊。


?3.4 自定義刪除器

std::shared_ptr 支持自定義刪除器,這使得它可以管理不僅僅是new分配的內存:

// 使用自定義刪除器管理文件指針
auto file_deleter = [](FILE* fp) { if(fp) fclose(fp); };
std::shared_ptr<FILE, decltype(file_deleter)> file_ptr(fopen("test.txt", "r"), file_deleter);

在這里插入圖片描述

🎄四、使用的幾個注意點

?4.1 不要將棧地址傳遞給 std::shared_ptr指針

std::shared_ptr 指針會在銷毀時調用默認的刪除器銷毀指針,默認的刪除器一般是 deletedelete[],如果將棧地址給到 std::shared_ptr指針后,對象銷毀時會出錯,看下面例子:

// shared_ptr_Stack.cpp
#include <iostream>
#include <memory>int main()
{int a = 5;std::cout << "&a=" << &a << std::endl;std::shared_ptr<int> u_Stack(&a);std::cout << "u_Stack=" << u_Stack.get() << ", value=" << *(u_Stack.get()) << std::endl;return 0;
}

運行結果:
在這里插入圖片描述


?4.2 std::shared_ptr 指針作為函數參數和返回值

作為函數參數:需要先將指針轉換為右值。
作為函數返回值:因為非引用返回值本身就是右值,可以直接返回。

舉例子:

// shared_ptr_Func.cpp
#include <iostream>
#include <memory>struct D {    // a verbose array deleter:void operator()(int* p) {if(p){delete p;std::cout << "delete." << " p=" << p << std::endl;}}
};// 作為參數引用計數加1
void use_count_add(std::shared_ptr<int> ptr) {std::cout << "ptr=" << ptr.get() << ", count=" << ptr.use_count() << std::endl;
} // ptr自動釋放,引用計數減一// 作為返回值引用計數不變,應該是調用了移動賦值運算符
std::shared_ptr<int> create_int(int value) {std::shared_ptr<int> uRet(new int(value));std::cout << "uRet=" << uRet.get() << ", count=" << uRet.use_count() << std::endl;return uRet;
}int main()
{std::shared_ptr<int> u_Param(new int(1), D());std::cout << "u_Param=" << u_Param.get() << ", value=" << *(u_Param.get()) << std::endl;use_count_add(u_Param);	// 引用計數加1std::cout << "u_Param=" << u_Param.get() << ", count=" << u_Param.use_count() << std::endl;std::shared_ptr<int> u_Return = create_int(3);std::cout << "u_Return=" << u_Return.get() << ", count=" << u_Return.use_count() << std::endl;return 0;
}

運行結果:
在這里插入圖片描述


?4.3 優先使用make_shared

C++11就已經有了 make_shared 函數,創建對象時,應該優先使用 make_shared ,提升性能與異常安全。

函數原型:

template <class T, class... Args>  shared_ptr<T> make_shared (Args&&... args);

make_shared的功能時,分配并構造一個T類型的對象,將參數傳遞給其構造函數,并返回一個shared_ptr類型的對象。該對象擁有并存儲指向它的指針(使用計數為1)。

舉例子:

// make_shared example
#include <iostream>
#include <memory>int main () {std::shared_ptr<int> foo = std::make_shared<int> (10);// same as:std::shared_ptr<int> foo2 (new int(10));auto bar = std::make_shared<int> (20);auto baz = std::make_shared<std::pair<int,int>> (30,40);std::cout << "*foo: " << *foo << '\n';std::cout << "*bar: " << *bar << '\n';std::cout << "*baz: " << baz->first << ' ' << baz->second << '\n';return 0;
}

運行結果:
在這里插入圖片描述


?4.4 避免重復接管原始指針

始終通過 shared_ptr 傳遞所有權。

int* raw = new int(10);
shared_ptr<int> p1(raw);
// shared_ptr<int> p2(raw);  // 錯誤!雙重釋放

?4.5 避免循環引用

問題:兩個 shared_ptr 互相引用,導致計數無法歸零。

解決:使用 weak_ptr 打破循環。

class B;
class A {shared_ptr<B> b_ptr;
};
class B {weak_ptr<A> a_ptr;  // 使用 weak_ptr
};

在這里插入圖片描述

🎄五、總結

std::shared_ptr 是 C++ 內存管理的核心工具之一,正確使用可顯著降低內存泄漏風險。理解其成員函數、引用計數機制及使用場景,能幫助開發者編寫更安全、高效的代碼。對于復雜場景,結合 weak_ptr 和 make_shared,可進一步提升程序的健壯性。

在這里插入圖片描述
如果文章有幫助的話,點贊👍、收藏?,支持一波,謝謝 😁😁😁

參考:
C++ 智能指針詳解:std::unique_ptr、std::shared_ptr 和 std::weak_ptr

https://cplusplus.com/reference/memory/shared_ptr/

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/905581.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/905581.shtml
英文地址,請注明出處:http://en.pswp.cn/news/905581.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

【CF】Day59——Codeforces Round 914 (Div. 2) D

D. Set To Max 題目&#xff1a; Easy 思路&#xff1a; 簡單題 由于題目的數據給的很小&#xff0c;所以我們可以用 n 的復雜度過&#xff0c;那我們來觀察一下我們應該怎么操作 顯然&#xff0c;如果 a[i] > b[i] 時是無法構造的&#xff0c;同時 a[i] b[i] 時就不用管…

解密企業級大模型智能體Agentic AI 關鍵技術:MCP、A2A、Reasoning LLMs- GPT源代碼解析

解密企業級大模型智能體Agentic AI 關鍵技術:MCP、A2A、Reasoning LLMs- GPT源代碼解析 我們可以稍微看一下, 這是我們GPT的基于它的源代碼產生的可視化的內容。 這邊是model ,我們在談這個sampling的時候,本身首先就是說它這個probability distribution ,會有很多的參數…

AI 推理 | vLLM 快速部署指南

本文是 AI 推理系列的第一篇&#xff0c;近期將更新與 vLLM 的相關內容。本篇從 vLLM 的部署開始&#xff0c;介紹 vLLM GPU/CPU 后端的安裝方式&#xff0c;后續將陸續講解 vLLM 的核心特性&#xff0c;如 PD 分離、Speculative Decoding、Prefix Caching 等&#xff0c;敬請關…

Python-MCPInspector調試

Python-MCPInspector調試 使用FastMCP開發MCPServer&#xff0c;熟悉【McpServer編碼過程】【MCPInspector調試方法】-> 可以這樣理解&#xff1a;只編寫一個McpServer&#xff0c;然后使用MCPInspector作為McpClient進行McpServer的調試 1-核心知識點 1-熟悉【McpServer編…

Linux 常用命令 -hostnamectl【主機名控制】

簡介 hostnamectl 命令中的 “hostname” 顧名思義&#xff0c;指的是計算機在網絡上的名稱&#xff0c;“ctl” 是 “control” 的縮寫&#xff0c;意味著控制。hostnamectl 命令用于查詢和修改系統主機名以及相關的設置。它通過與 systemd 系統管理器交互&#xff0c;允許用…

力扣-二叉樹-101 對稱二叉樹

思路 分解問題為&#xff0c;該節點的左孩子的左子樹和右孩子的右子樹是不是同一棵樹 && 該節點的左孩子的右字數和右孩子的左子樹是不是同一課樹 && 該節點的左右孩子的值相不相同 代碼 class Solution {public boolean isSymmetric(TreeNode root) {// 層…

Nginx技術方案【學習記錄】

文章目錄 1. 需求分析1.1 應用場景1.2 實現目標 2. Nginx反向代理與實現均衡負載2.1 部署架構2.2 架構描述2.2.1 Nginx代理服務器2.2.2 API服務器與API服務器&#xff08;Backup&#xff09;2.2.3 nginx.conf配置文件2.2.4 測試方法 3. 高速會話緩存技術3.1 問題背景3.2 使用 R…

Ubuntu22.04怎么退出Emergency Mode(緊急模式)

1.使用nano /etc/fstab命令進入fstab文件下&#xff1b; 2.將掛載項首行加#注釋掉&#xff0c;修改完之后使用ctrlX退出; 3.重啟即可退出緊急模式&#xff01;

Unity 紅點系統

首先明確一個&#xff0c;即紅點系統的數據結構是一顆樹&#xff0c;并且紅點的數據結構的初始化需要放在游戲的初始化中&#xff0c;之后再是對應的紅點UI側的注冊&#xff0c;對應的紅點UI在銷毀時需要注銷對紅點UI的顯示回調注冊&#xff0c;但是不銷毀數據側的紅點注冊 - …

極新攜手火山引擎,共探AI時代生態共建的破局點與增長引擎

在生成式AI與行業大模型的雙重驅動下&#xff0c;人工智能正以前所未有的速度重構互聯網產業生態。從內容創作、用戶交互到商業決策&#xff0c;AI技術滲透至產品研發、運營的全鏈條&#xff0c;推動效率躍升與創新模式變革。然而&#xff0c;面對AI技術迭代的爆發期&#xff0…

【Redis】SDS結構

目錄 1、背景2、SDS底層實現 1、背景 redis作為高性能的內存數據庫&#xff0c;對字符串操作&#xff08;如鍵、值的存儲&#xff09;有極高的要求。c語言原生字符串&#xff08;以\0結尾的字符串數據&#xff09;有一些缺點&#xff1a;長度計算需要遍歷&#xff08;O(n)時間…

STM32硬件I2C驅動OLED屏幕

本文基于STM32硬件I2C驅動SSD1306 OLED屏幕&#xff0c;提供完整的代碼實現及關鍵注意事項&#xff0c;適用于128x32或128x64分辨率屏幕。代碼通過模塊化設計&#xff0c;支持顯示字符、數字、漢字及位圖&#xff0c;并優化了顯存刷新機制。 零、完整代碼 完整代碼: 1&#x…

鴻蒙 PC 發布之后,想在技術上聊聊它的未來可能

最近鴻蒙 PC 剛發布完&#xff0c;但是發布會沒公布太多技術細節&#xff0c;基本上一些細節都是通過自媒體渠道獲取&#xff0c;首先可以確定的是&#xff0c;鴻蒙 PC 本身肯定是無法「直接」運行 win 原本的應用&#xff0c;但是可以支持手機上「原生鴻蒙」的應用&#xff0c…

【JAVA】抽象類與接口:設計模式中的應用對比(16)

核心知識點詳細解釋 Java抽象類和接口的定義、特點和使用場景 抽象類 抽象類是使用 abstract 關鍵字修飾的類。它不能被實例化&#xff0c;主要用于作為其他類的基類&#xff0c;提供一些通用的屬性和方法。抽象類可以包含抽象方法和具體方法。抽象方法是使用 abstract 關鍵…

HTML 顏色全解析:從命名規則到 RGBA/HSL 值,附透明度設置與場景應用指南

一、HTML 顏色系統詳解 HTML 中的顏色可以通過多種方式定義&#xff0c;包括顏色名稱、RGB 值、十六進制值、HSL 值等&#xff0c;同時支持透明度調整。以下是詳細分類及應用場景&#xff1a; 1. 顏色名稱&#xff08;預定義關鍵字&#xff09; HTML 預定義了 140 個標準顏色名…

LVS負載均衡群集和keepalive

目錄 一. 集群概述 1.1 集群的定義 1.2 集群的分類 1. 高可用集群 HA 2. 高性能運輸群集 HPC 3.負載均衡群集 LB 4. 分布式存儲集群 二. LVS概述 2.1 LVS的定義 2.2 LVS的工作原理 2.3 LVS 的三種工作模式 2.4 LVS 三種工作模式的對比 2.5 LVS 調度算法 1. 靜態…

ZTE 7551N 中興小鮮60 遠航60 努比亞小牛 解鎖BL 刷機包 刷root 展訊 T760 bl

ZTE 7551N 中興小鮮60 遠航60 努比亞小牛 解鎖BL 刷機包 刷root 3款機型是一個型號&#xff0c;包通用&#xff0c; ro.product.system.modelZTE 7551N ro.product.system.nameCN_P720S15 #################################### # from generate-common-build-props # Th…

單片機-STM32部分:12、I2C

飛書文檔https://x509p6c8to.feishu.cn/wiki/MsB7wLebki07eUkAZ1ec12W3nsh 一、簡介 IIC協議&#xff0c;又稱I2C協議&#xff0c;是由PHILP公司在80年代開發的兩線式串行總線&#xff0c;用于連接微控制器及其外圍設備&#xff0c;IIC屬于半雙工同步通信方式。 IIC是一種同步…

Virtualized Table 虛擬化表格 el-table-v2 表頭分組 多級表頭的簡單示例

注意添加這個屬性,會影響到有多少個層級的表頭: :header-height“[50, 40]”,即后面的columnIndex 如果有fix的列CustomizedHeader會被調用多次,如果有多個層級的表頭,也會被調用多次, 實際被調用次數是(fix數 1 * 表頭層級數量) 以下代碼均刪除了JSX TS版本代碼 <templ…

防御保護-----第十二章:VPN概述

文章目錄 第二部分&#xff0c;數據安全第十二章&#xff1a;VPN概述VPN概述VPN分類VPN關鍵技術隧道技術身份認證技術加解密技術數據認證技術 數據的安全傳輸密碼學發展史 對稱加密算法 --- 傳統密碼算法密鑰解釋流加密分組加密 --- 塊加密算法填充算法PKCS7算法分組模式 公鑰密…