【C++】智能指针详解

一、从new和delete谈起

在C++中,可以使用new和delete关键字进行对象的创建和销毁,new一个对象实际上是在堆上分配内存,而new出来的对象也要自己用delete释放,从而回收内存,否则会造成内存的泄露。由程序员自己new来分配对象内存的方式成为动态分配。

其中有两个要点

  • new和delete需要成对使用,有new必然有delete,否则会导致内存泄漏,没有new分配的内存,不能使用delete来释放。
  • delete一块内存,只能delete一次,不可以delete多次,因为第一次delete之后,这块内存被回收会被分配给其他变量。可以在delete一个指针后将该指针设置为空ptr = nullptr。因为一个指针指向的内容即便被delete,该指针中依然保存着它所指向的那块动态内存地址,此时该指针被称为选空指针。如果此时将指针置空,则表示该指针不指向任何内存,这是一个良好的编程习惯

在使用动态分配时经常会有以下问题:

  • 内存泄漏:new了的对象再使用完后忘记delete导致内存一直被占用,相当于吃完饭不收盘子
  • 重复删除:对已经delete的对象进行再次delete,但是原内存空间被分配给了其他对象,导致回收到了其他的内存空间,相当于吃完饭把别人的盘子收了
  • 回收冲突:A和B均需要使用对象a,A使用完之后就顺手delete了a,但是B还需要使用a。相当于别人没吃完自己吃完,先把盘子收走了

总的来说,对内存进行管理依然是会让初学者头大的一个挑战,因此C++新标准中出现了智能指针。

二、智能指针

直接使用new一个对象的方式返回的是一个对象的指针,这个对象指针称为裸指针,这种指针功能强大使用灵活,但是需要全程负责维护,容易出错。在C++ 11中引入了新的指针——智能指针,智能指针对裸指针进行了包装,最突出的特性是智能指针能够自动释放所指向的对象。

C++标准库中规定了四种智能指针,分别是std::auto_ptr, std::unique_ptr, std:shared_ptr, std:weak_ptr,其中std::auto_ptr是C++ 98推出的,目前该智能指针已被std:unique_ptr取代,C++ 11中也明确弃用了该种智能指针。这三种指针其实都是类模板,可以将new获得的地址赋予给他们

  • shared_ptr是共享指针的概念,多个指针指向同一个对象,最后一个指针被销毁时,这个对象会被释放。
  • weak_ptr主要用于辅助shared_ptr工作
  • unique_ptr是一种独占式指针的概念,同一个时间内只能由一个指针指向该对象

2.1 shared_ptr

shared_ptr指针采用共享所有权来管理所指向对象的生存期,对象可以被多个shared_ptr指向,多个shared_ptr之间互相协作,从而确保只在不需要所指对象的时候才回收对象。shared_ptr的工作机制是引用计数,每一个shared_ptr指向相同的对象都会增加引用计数值,知道最后一个指向该对象的shared_ptr指针不再指向该对象的时候,才会析构对象

智能指针是一个类模板,使用尖括号规定指针指向的类型,使用形式如下

shared_ptr<指向的类型> 智能指针名

// 样例
shared_ptr<string> p1; //一个指向string的智能指针

当然,智能指针也可以进行带初值的初始化

shared_ptr<int> pi(new int(100));

这是使用裸指针初始化智能指针的场景,但是还会遇到一些陷阱,因此shared_ptr模板中内置了make_shared函数进行初始化,是最安全并且更高效的初始化方法,它能够在动态内存(堆)中分配并且初始化一个对象,然后返回指向此对象的shared_ptr,使用方法如下:

shared_ptr<int> p2 = std::make_shared<int>(10);
shared_ptr<string> p3 = std::make_shared<string>(10, 'hello');

2.1.2 shared_ptr常规操作

1.引用计数的增加
每个shared_ptr会记录有多少个其他shared_ptr指向同样的对象
(1) 使用智能指针对另一个智能指针进行初始化

auto p6 = std::make_shared<int>(100);
auto p7 = p6;

(2)把智能指针作为实参往函数中传递

void myfunc(shared_ptr<int> ptmp){
	return;
}

myfunc(p7);

这会增加指针引用数,当函数返回时减少
(3)作为函数返回值

shared_ptr<int> myfunc(shared_ptr<int> ptmp){
	return ptmp;
}

p8 = myfunc(p7);

2.引用计数减少
(1)shared_ptr指向新对象的时候,引用计数减少
(2)局部智能指针离开其作用域,比如上面(2)中智能指针作为实参传递进函数时增加引用数,函数执行完毕后减少
(3)当一个shared_ptr引用技术变为0的时候,会自动释放自己管理的对象

3.常规操作
(1)use_count成员函数:返回有多少个智能指针指向某个对象
(2)unique成员函数,是否仅有一个智能指针指向某对象
(3)reset成员函数,不带参数时,pi置空,原对象引用计数-1;带参数则将该智能指针指向参数中的对象

*4.解引用
返回智能指针p指向的对象

5.get成员函数
p.get()用于返回智能指针p中保存的指针,小心使用,若智能指针释放了所指向的对象,则返回的指针所指的对象也会失效。

2.2 weak_ptr简介

weak_ptr是一个智能指针,这种智能指针指向一个由shared_ptr管理的对象,但是这种指针并不控制所指向对象的生存期,也不会改变shaed_ptr的引用计数。使用样例如下:

auto pi = make_shared<int>(100);
weak_ptr<int> piw(pi);

既然weak_ptr所指向的对象有可能不存在,那么waek_ptr是不能直接用于访问对象的,必须要使用一个叫做lock的成员函数,lock的功能是检查weak_ptr所指向的对象是否还存在,如果是,lock能够返回一个指向共享对象的shared_ptr,如果不存在,则返回一个空的shared_ptr

常用操作
1.use_count成员函数
获取该弱指针共享对象的其他shared_ptr数量,或者说强引用计数的数量

2.expired
若该指针的use_count为0,则返回true,反则返回false

3.reset成员函数
将该弱指针设置为空,不影响该对象的强引用数量,但是指向该对象的弱引用数量减1

4.lock
获取一个弱指针所指向的对象的shared_ptr

2.2.1 shared_ptr和weak_ptr的大小

weak_ptr和shared_ptr的尺寸都是裸指针的2倍大小,在x86平台下,一个裸指针的sizeof时4Bytes,因此weak_ptr和shared_ptr的大小都是8Bytes。其实这8个字节包含了2个裸指针,如图示:
在这里插入图片描述
其中第一个裸指针指向智能指针所指向的对象,第二个指针指向一个控制块,这个控制块中有:1.所指对象的引用计数 2.所指对象的弱引用计数 3.其他数据,比如自定义的删除器指针

2.3 unique_ptr简介和常规操作

2.3.1 unique_ptr简介

unique_ptr智能指针是一种独占式智能指针,同一个时刻,只有一个unique_ptr指针指向这个对象,当这个unique_ptr被销毁的时候,它所指向的对象也会被销毁。

1.常规初始化(unique_ptr和new配合)

unique_ptr<int> pi;
unique_ptr<int> pi2(new int(105));

2.make_unique函数
C++ 11中没有make_unique函数,但是C++ 14提供了这个函数
和常规的初始化相比,要优先选择make_unique函数,这能有更好的性能。但是如果使用make_unqiue的话,就不能使用删除器,因为make_unique不支持指定删除器的语法

unique_ptr<int> p1 = std::make_unique<int>(100);
auto p2 = std::make_unique<int>(200);

2.3.2 unique_ptr常用操作

1.unique_ptr不支持的操作

unique_ptr<string> ps1(new string("I Love China!));
unique_ptr<string> ps2(ps1);	// 不支持复制
unique_ptr<string> ps3 = ps1;	// 不支持复制
unique_ptr<string> ps4;
ps4 = ps1;	// 不支持赋值动作

unique_ptr不允许复制、赋值等操作,是一个只能移动不能复制的语义

2.移动语义
可以通过std:move将一个unique_ptr转移到其他的unique_ptr中

unique_ptr<string> ps1(new string("I Love China!));
unique_ptr<string> ps3 = std::move(ps1);	// 转移后ps3为空了,ps3指向原来的ps1所指

3.release成员函数
放弃对指针的控制权,切断智能指针和其所指向对象之间的联系。返回指针,将智能指针置空,返回的裸指针可以手工delete食坊,也可以用于初始化另一个智能指针,或者给另外一个智能指针赋值。

4.reset成员函数
当reset不带参数的时候,释放智能指针指向对象,并且将智能指针置空,当reset带参数时,释放智能指针指向原来的内存,让该智能指针指向新内存。

5.=nullptr
释放智能指针所指向的对象,并且将智能指针置空

6.指向一个数组

std::unique_ptr<int[]> ptr_arr(new int[10]);
ptr_arr[0] = 12;
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值