C++11智能指针【shared_ptr | weak_ptr | unique_ptr】

shared_ptr

一、创建shared_ptr对象

1.单个对象

shared_ptr<int>pa = make_shared<int>(100);//pa指向的内存初始化为100
//或share_ptr<int>pa(new int(100));
										  //该内存的strong ref = 1
shared_ptr<int>pb(pa);					  //pb与pa指向同一块内存
										  //此时,有两个shared_ptr对象指向该内存,故该内存的strong ref 变为2
										  

2.数组

class A
{
	public:
	A(){}	//构造函数
	~A(){}	//析构函数
	//...
};
int maint(void)
{
	//正确的写法,会调用会调用每个A对象的构造函数和析构函数
	shared_ptr<A[]>Aarr(new A[10]);	//创建10个A对象
	
	//错误的写法:程序结束时不会调用所以数组里的对象的析构函数,引发异常
	//shared_ptr<A>Aarr(new A[10]);
	
	//另一个正确的写法:
	shared_ptr<A>arrA(new A[10], default_delete<A[]>());
}

二、观察shared_ptr对象

cout<<sizeof(pa)<<endl;	//8 [win 32]

我们在win 32上打印普通指针的大小为4,比如:

int* p;					//一个普通指针
cout<<sizeof(p)<<endl;	//4

那么为什么shared_ptr对象的大小就是8呢?
接下来我们把普通指针称为裸指针

这是因为,shared_ptr不仅存有一个裸指针指向一个内存,而且该裸指针指向的内存也有一些信息需要share_ptr的一个指针去指向。
如下图:
在这里插入图片描述
在上面的代码中,pa和pb里指向数据的指针都指向了中间的同一个数据内存块,该数据内存块相关的信息内存块也因pb指向该数据内存块而发生变化,比如信息内存里的strong ref增加1,表示又有一个shared_ptr指向该数据内存块

三、shared_ptr相关函数

1.use_count
判断有几个share_ptr指向同一块内存【数据块和相关信息块】
unique() 判断是否只有一个share_ptr指向该内存,返回1:是;返回0:否

shared_ptr<int>pa = make_shared<int>(100);
shared_ptr<int>pb(pa);
cout<<pa.use_count()<<endl;	//2
cout<<pb.use_count()<<endl; //也是2
if(pa.unique())
	cout<<"unique"<<endl;
else
	cout<<"not unique"<<endl;
//将打印not unique

2.reset()
这个函数干了这几件事:①该shared_ptr对象指向的内存块的信息中strong ref减1 ②如果该shared_ptr对象指向的内块信息的strong ref变为0,则清空该内存块 ③清空该shared_ptr对象

shared_ptr<int>pa = make_shared<int>(100);		//strong ref = 1
shared_ptr<int>pb(pa);							//strong ref = 2
pa.reset();										//strong ref = 1,pa被清空,但内存还在
cout<<pb.unique()<<endl;						//1
pb.reset();										//strong ref = 0,该内存被释放,pb也被清空
//pb = nullptr;//也可以用这个语句来代替pb.reset()

3.get()
返回该shared_ptr对象的裸指针,也就是返回下图中橙色的这块
在这里插入图片描述

shared_ptr<int>pa = make_shared<int>(100);		
cout<<*pa<<endl;		//100
int *nakedP = pa.get();
cout<<*nakedP<<endl;	//100
//千万别这么干: delete nakedP;//引发异常

4.swap

shared_ptr<string>pa = make_shared<string>("Rose");
shared_ptr<string>pb = make_shared<string>("Jack");
pa.swap(pb);	//两者指向的内存对换
swap(pa,pb);	//现在又换回来了

四、删除器

shared_ptr指向的内存会在程序结束后自动释放,或者在strong ref=0时释放。这些都是缺省的释放方法,我们也可以在创建shared_ptr对象时定义自己的释放函数(删除器),比如在释放时简单地打印一条信息
1.

 `shared_ptr<int>pa (new int(100), myDelete);  					//myDelete是自己定义的函数,注意这里只能用new,不能用make_shared
 `

myDelete定义如下:

void myDelete(int *p)
{
	cout<<"myDelete执行了"<<endl;
	delete p;
}

2.也可以用lambda表达式代替

make_shared<int>pa(new int(100), [](int *p)
									{
										cout<<"lambda表达式删除器"<<endl;
										delete p;
									});

五、移动语义

shared_ptr<int>pa = make_shared<int>(100);
shared_ptr<int>pb(move(pa));	//清空pa。由pb指向该内存

六、用法谨记

1.不要用同一个裸指针初始化多个shared_ptr对象
下面是一段引发异常的代码

	int* p = new int(100);
	shared_ptr<int>p1(p);
	shared_ptr<int>p2(p);

在上面代码中,如果在创建p1后立刻调用p1.reset(); 那么就不会引发异常

2.避免套娃
有两个类,CA和CB

class CB;
class CA
{
public:
	~CA()
	{
		cout << "~CA()" << endl;
	}
	shared_ptr<CB> mpb;
};

class CB
{
public:
	~CB()
	{
		cout << "~CB()" << endl;
	}
	shared_ptr<CA> mpa;
};

在main()中
这么使用这两个类会导致无法调用这两个类的析构函数,引发内存泄露

	shared_ptr<CA>a(make_shared<CA>());
	shared_ptr<CB>b(make_shared<CB>());
	
	a->mpb = b;
	b->mpa = a;
	cout << a.use_count() << endl;
	cout << b.use_count() << endl;
	//要加下面这两行才可以调用析构函数
	//a->mpb.reset();
	//b->mpa.reset();

分析:a和b两个类类型的share_ptr,要等到各自指向内存的strong ref变为0时才能被释放,执行析构函数,而a的成员mpb和b的成员mpa都要由析构函数来释放,这样就造成了“死锁”。所以,如果我们显式地调用最后两个类对象分别的mpb和mpa成员,让strong ref都降为1,才可以指向该类的析构函数。

weak_ptr

weak_ptr可以用来辅助shared_ptr的使用,怎么辅助呢,请接着往下阅读:

一、相关函数

1.lock() 和 use_count()以及expired()
1)lock(): 先用一个shared_ptr来初始化一个weak_ptr对象,那么该weak_ptr对象的一个指针也指向了下图中蓝色的区域:
在这里插入图片描述
该weak_ptr调用lock函数,对应内存的strong ref >=1,则返回一个指向该内存的shared_ptr对象,若对应内存的strong ref =0,则返回一个空的share_ptr

2)use_count()返回指向对应内存的shared_ptr对象的数量
3)expired():如果没有shared_ptr指向该内存,则返回1,若有则返回0

shared_ptr<int>p = make_shared<int>(100);
	weak_ptr<int>weak_p1(p);					//strong ref不因weak_p改变,但weak ref增1
	cout << sizeof(weak_p1) << endl;			//8
	//weak_ptr的lock()函数:weak_ptr 指向的shared_ptr对象存在,则返回该shared_ptr对象且strong ref增1,否则,返回一个空的share_ptr,strong ref不增
	auto pret = weak_p1.lock();
	if (pret != nullptr)
	{
		cout << "weak_p1指向的对象存在" << endl;
	}
	cout << weak_p1.use_count() << endl;			//获得strong ref 数   2
	p.reset();
	pret.reset();
	cout << weak_p1.expired() << endl;				//1  判断指向的对象是否存在

	weak_p1.reset();								//weak ref 置0,清空weak_p1,不影响strong ref

unique_ptr

一、创建unique_ptr对象

1.普通的对象

unique_ptr<int>p1 = make_unique<int>(100);			//声明并初始化为100
unique_ptr<int>p2(new int(90));						//初始化为90
unique_ptr<string>p3 = make_unique<string>("OKOK");	//初始化为“OKOK”
unique_ptr<string>p4(new string("YESYES"));			//初始化为“YESYES”

2.指向数组的对象

unique_ptr<int[]>arrP(new int[5]);
arrP[0] = 99;
cout << arrP[0] << endl;		//99

3.自定义删除器的对象
1)函数删除器
定义一个删除器函数如下:

void myDelete(int* p)
{
	delete p;
	cout<<"调用了myDelete"<<endl;
}

在main()中:

using fp = void(*)(int*);
//或 typedef void(*fp)(int*);
//或 typedef decltype(myDelete) (*fp);
unique_ptr<int,fp>pa(new int(15),myDelete);

2)lambda表达式删除器

auto myLambda = [](int *p){
					cout<<"myLambda删除器"<<endl;
					delete p;
						};
unique_ptr<int,decltype(myLambda)> pb (new int(16),myLambda);

二、观察unique_ptr对象

1.删除器会影响unique_ptr对象的大小

unique_ptr<int>p1 = make_unique<int>(99);
unique_ptr<int,fp>pa(new int(15),myDelete);	//mydelete定义在上文
unique_ptr<int,decltype(myLambda)> pb (new int(16),myLambda);//myLambda定义在上文
cout<<sizeof(p1)<<endl;		//4
cout<<sizeof(pa)<<endl;		//8
cout<<sizeof(pb)<<endl;		//4

三、相关函数

reset():释放该unique_ptr对象指向的内存,并置空该unique_ptr对象

reset(裸指针参数):解除该uniuqe_ptr与原来指向内存的关系,清空原来指向的内存,并重新指向裸指针参数指向的内存

release():返回一个裸指针,并解除该内存与该unique_ptr对象的联系,置空该unique_ptr对象

unique_ptr<int>p1 = make_unique<int>(10);
unique_ptr<int>p2= make_unique<int>(20);
p1.reset(new int(1));	//清空p1原来指向存放10的内存,置空p1
						//让p1指向新内存,新内存存着1
p1.reset();				//清空p1指向的存着1的内存,置空p1
p1.reset(p2.release());	//p2.release()返回指向存着20的内存的裸指针,置空p2。p1指向该裸指针指向的内存

四、函数返回unique_ptr和移动语义

1.移动语义

unique_ptr<int>p1 = make_unique<int>(33);
unique_ptr<int>p2(move(p1));
//现在p1被置空,p2指向存着33的内存

2.函数返回unique_ptr
uniuqe_ptr对象指向的内存不可被多个unique_ptr对象共享,但是可以拷贝即将要被销毁的unique_ptr,比如接受函数返回,如下:

定义一个函数:

unique_ptr<int> myFunc()
{
	return make_unique<int>(100);
}

在main()中

unique_ptr<int>p = myFunc();
cout<<*p<<endl;					//100

五、其他注意点

1.不同删除器的uniuqe_ptr不可相互swap或装入通一个容器

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值