【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.mzph.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算法分组模式 公钥密…