【C++11】智能指针

智能指针引入

当我们编写程序时,使用动态内存分配获取了堆区的内存,一定不要忘了释放动态获取的内存,因为可能导致内存泄漏。

但这种设定有一定缺陷:

1.对操作要求严格。要申请就不能忘记释放,但需要人工介入的行为很难保证无差错;
2.对运行环境要严格。即使写好了释放内存的语句,但也可能因为程序运行异常,导致有申请而无释放的情况;
3.耗费人力。有时候会有多个指针指向同一个对象,释放对象不正确或释放时机不正确都会导致程序报错。

假设有一种指针,它是一个对象,在它过期后会调用自身的析构函数自动释放它指向区域的内存。这正是我们想要解决指针管理不易的解决方案,也是智能指针背后的思想。

智能指针简介

智能指针是行为类似于指针的类对象,可以帮助管理动态分配的内存。其支持解引用和箭头运算符等基本操作。

C++11引入了3种智能指针,shared_ptr,unique_ptr和weak_ptr。

使用智能指针需要引入头文件<memory>

unique_ptr

unique_ptr建立所有权的概念,对于指向的对象,只能有一个unique_ptr智能指针拥有它。

由于只能有一个unique_ptr智能指针拥有它,unique_ptr不支持拷贝和赋值。

建议使用make_unique<T>()方式来生成unique_ptr,如果T类型的构造函数抛出异常的话你可能会内存泄露,make_unique<T>()方式兼顾异常安全。

unique_ptr初始化:

/* unique_ptr初始化 */
// 空智能指针
unique_ptr<int> up1;

// 使用内置指针初始化
unique_ptr<double> up2(new double(1.0));

// 推荐使用 make_unique<T>(),此方法兼顾异常安全
shared_ptr<int> up = make_unique<int>();
unique_ptr常用方法
// 相互独立的智能指针
unique_ptr<int> sp(new int(42));

// 放弃对指针的控制权,返回指针并将sp置为空。注意:此操作并不会释放原对象,可能需要手动释放
sp.release();
// 释放当前对象并接管新对象
sp.reset(new int(18));
// 释放当前对象
sp.reset();
unique_ptr拷贝将亡值

前文提到unique_ptr不支持拷贝和赋值但有一种情况除外–将亡值。即等号有边的对象声明周期即将结束,常见将亡值有:非引用的函数返回值和匿名对象。

例如:

/* 将亡值拷贝 */
unique_ptr<string> test()
{
    return unique_ptr<string>(new string("hello"));
}
unique_ptr托管动态数组

unique_ptr 可以用来托管数组,并且在析构时会自动调用 delete[],而不是 delete。

/* 使用unique_ptr管理动态数组 */
unique_ptr<int[]> arr = make_unique<int[]>(5);

for (int i = 0; i < 5; ++i) {
    arr[i] = i * 10;
}

for (int i = 0; i < 5; ++i) {
    cout << arr[i] << " ";  // 输出 0 10 20 30 40
}
shared_ptr

shared_ptr跟踪引用特定对象的智能指针数,称之为引用计数(reference counting)。当有一个新的智能指针指向当前对象时,计数将加1;反之,当指向当前对象的智能指针指向别处时,计数将减1。当引用计数为0时,智能指针指向的对象的内存会被释放掉。

引用计数增加:
在这里插入图片描述

引用计数减少:
在这里插入图片描述
shared_ptr在内存中分配一块区域用于引用计数,使用make_shared<T>()初始化,引用计数块和指针内存同时分配效率更高。
shared_ptr初始化:

/* shared_ptr初始化 */

// 空智能指针
shared_ptr<int> sp1;
// 使用内置指针初始化
shared_ptr<double> sp2(new double(1.0));
// 使用 make_shared<T>() 初始化 引用技术块和指针内存同时分配效率更高
shared_ptr<double> sp3 = make_shared<double>();
// 使用智能指针初始化
shared_ptr<int> sp4 = sp1;
相互独立的shared_ptr

智能指针多次使用内置指针初始化shared_ptr时会导致产生相互独立的shared_ptr–多个智能指针并不知道对方存在。相互独立的shared_ptr会产生提前释放对象的行为,其中一个shared_ptr指针释放了对象导致其它shared_ptr指针指向未定义区域(已被释放区域)。

正确的初始化方式是:仅使用内置指针初始化一次,再次需要初始化时使用智能指针相互初始化。

/* 分别多此使用内置类型指针初始化智能指针会导致生成相互独立的智能指针 */

shared_ptr<int> sp1(new int(3));
// 使用get()方法获取智能指针所监管内置指针(裸指针),此方法应尽量少的使用
shared_ptr<int> sp2(sp1.get());

// sp1指向空值导致原对象被释放
sp1 = nullptr;
// 此时sp2所指区域已经被释放,对其解引用是未定义行为
cout << (*sp2) << endl;
shared_ptr常用方法
/* shared_ptr常用方法 */
shared_ptr<int> sp = make_unique<int>();

// 获取智能指针帮助管理的指针,此方法应尽量少的使用
sp.get();
// 所指向对象引用计数为1时返回true
sp.unique();
// 返回所指向对象的引用计数
sp.use_count();
// 释放当前对象并接管新对象
sp.reset(new int(18));
// 释放当前对象
sp.reset();
shared_ptr托管动态数组

unique_ptr 可以用来托管数组,并且在析构时会自动调用 delete[],而不是 delete。

尽管 shared_ptr 也可以托管数组,但由于 shared_ptr 的设计并不是特别针对数组,它需要自定义删除器来正确管理数组内存。

建议尽量不要使用share_ptr管理动态数组。

注意同unique_ptr区分:

  • shared_ptr 托管数组时,必须传入自定义删除器(如 delete[]),否则它会错误地使用 delete 而不是 delete[],导致未定义行为。
  • 使用 arr.get() 来获取裸指针并访问数组。
/* 使用share_ptr管理动态数组 */
shared_ptr<int> arr(new int[5], [](int* p) { delete[] p; });  // 使用自定义删除器

for (int i = 0; i < 5; ++i) {
    arr.get()[i] = i * 10;
}

for (int i = 0; i < 5; ++i) {
    cout << arr.get()[i] << " ";  // 输出 0 10 20 30 40
}
weak_ptr

weak_ptr是一种不控制所指向对象生存期的智能指针,它指向一个由shared_ptr管理的对象,将一个weak_ptr绑定到一个shared_ptr不会改变shared_ptr的引用计数。

weak_ptr它设计用来解决智能指针shared_ptr可能产生的循环引用问题。并且在某些场景下,可能只需要临时访问一个对象,并不需要对其进行长期持有,使用weak_ptr可以减少不必要的引用计数增加和减少操作,从而优化程序性能。

weak_ptr初始化:

// weak_ptr初始化
// 首先初始化一个shared_ptr
shared_ptr<int> p_shared = make_shared<int>();
// weak_ptr“弱引用”shared_ptr
weak_ptr<int> p_weak = sp_shared;
weak_ptr常用方法
/* weak_ptr常用方法 */
weak_ptr<int> wp = make_shared<int>();

// 将wp置空
wp.reset();
// 与wp共享对象的shared_ptr数量
wp.use_count();
// 若wp.use_count()为0返回true
wp.expired();
// 若expired()为false返回一个指向所管理对象的shared_ptr,否则返回空shared_ptr
wp.lock()
weak_ptr使用

由于weak_ptr绑定的对象可能不存在,因此使用前要通过lock()方法来判断是否weak_ptr所指对象是否存在。

// 若所指对象存在返回一个指向所管理对象的shared_ptr
// 否则返回空shared_ptr
auto sp = wp.lock()
  • 6
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Sophon、

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

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

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

打赏作者

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

抵扣说明:

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

余额充值