【C++ | 内存管理】C++ shared_ptr 详解:成员函数、使用例子和实现原理

😁博客主页😁:🚀https://blog.csdn.net/wkd_007🚀
🤑博客内容🤑:🍭嵌入式开发、Linux、C语言、C++、数据结构、音视频🍭
🤣本文内容🤣:🍭介绍 C++ 智能指针🍭
😎金句分享😎:🍭你不能选择最好的,但最好的会来选择你——泰戈尔🍭
⏰发布时间⏰:

本文未经允许,不得转发!!!


在这里插入图片描述

在这里插入图片描述

🎄一、概述

在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;                          // copy

  bar = std::make_shared<int> (20);   // move

  std::unique_ptr<int> unique (new int(30));
  foo = std::move(unique);            // move from unique_ptr

  std::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.get
  int* 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. bool
  std::cout << "foo_3: " << (foo_3?"not null":"null") << "\n\n";

  // 5.use_count
  std::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;  // empty

  sp.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 object

  return 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 201606
  using 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_DEPRECATED
  template<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 constrained
  template<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_DEPRECATED
  template<typename _Yp>
	_Assignable<auto_ptr<_Yp>>
	operator=(auto_ptr<_Yp>&& __r)
	{
	  this->__shared_ptr<_Tp>::operator=(std::move(__r));
	  return *this;
	}
#endif

  shared_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);	// 引用计数加1
	std::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/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

wkd_007

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值