😁博客主页😁:🚀https://blog.csdn.net/wkd_007🚀
🤑博客内容🤑:🍭嵌入式开发、Linux、C语言、C++、数据结构、音视频🍭
🤣本文内容🤣:🍭介绍 C++ 智能指针🍭
😎金句分享😎:🍭你不能选择最好的,但最好的会来选择你——泰戈尔🍭
⏰发布时间⏰:
本文未经允许,不得转发!!!
🎄一、概述
在C++11编程中,智能指针已经成为管理动态内存的标配工具。最开始的智能指针有 std::auto_ptr
,后面 C++11
标准出来后,std::auto_ptr
就废弃了,新增了三个智能指针:std::unique_ptr
、std::shared_ptr
、std::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 指针会在销毁时调用默认的删除器销毁指针,默认的删除器一般是 delete
或 delete[]
,如果将栈地址给到 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