C++基础总结系列之【智能指针】

目录

智能指针

what

why

how

auto_ptr

unique_ptr

shared_ptr

weak_ptr


智能指针

what

在C++中,对动态内存的管理是通过new和delete完成的,在使用时极其容易出现问题,因为确保在正确的时间释放内存是很难的,有时忘记释放内存,就会产生内存泄漏,有时在尚有指针引用内存的情况下就释放了内存,就会产生引用非法内存的指针的情况。为了更安全地使用动态内存,C++新标准库提供了智能指针来管理动态对象,它的行为类似于常规指针,重要的区别是它能够自动释放所指向对象的空间,并且它是封装好的模板类(引用自C++ Primer Page400)。


why

为了安全方便地使用动态内存,管理动态内存的开辟与释放,避免内存泄露。


how

智能指针分为四种:auto_ptr、shared_ptr、unique_ptr、weak_ptr


auto_ptr

这是一个C++98中的方案,由于具有设计缺陷,在C++11中已经被舍弃,但具有学习的价值。

初始化方法

  • 方法一:

std::auto_ptr<类型> p1(new 类型(值));

  • 方法二:

std::auto_ptr<类型> p1

p1.reset(new 类型(值));

  • auto_ptr设计的缺陷:

auto_ptr采用copy语义来转移指针资源,采用所有权模型,转移指针资源的所有权的同时将原指针置为NULL。

这跟通常理解的copy行为是不一致的(不会修改原数据),而这样的行为在有些场合下不是我们希望看到的。

下面为示例代码:

auto_ptr<int> p1(new int(100));
auto_ptr<int> p2;
p2.reset(new int(200));
p2 = p1;
cout<<p1.get()<<endl;
cout<<*p2<<endl;
cout<<*p1<<endl;

下面代码能通过编译,可以输出,输出的结果为:100,和00000000。即修改了p2的指向,指向p1原来指向的空间。

但随后会崩溃,原因是p1原来指向的空间,在通过p1给p2赋值时,已经被置为空,再次访问p1,操作了野指针,因此造成崩溃。

即:多个auto_ptr不能指向同一块内存。


unique_ptr

初始化方法

  • 方法一

std::unique_ptr<类型> p1(new 类型(值));

  • 方法二:

std::unique_ptr<类型> p1

p1.reset(new 类型(值));

  • 对于auto_ptr缺陷的解决方式
unique_ptr<int> p1(new int(100));
unique_ptr<int> p2;
p2.reset(new int(200));
p2 = p1;

上述代码在编译时候即已无法通过,unique_ptr,顾名思义,采用的是所有权模型,但多个unique_ptr不能指向同一块内存,通过这种方式避免了产生auto_ptr的缺陷问题。

unique_ptr<int> p1(new int(100));
unique_ptr<int> p2;
p2.reset(new int(200));
p2 = move(p1);
	
if(nullptr != p1.get())
	cout<<"p1:"<<*p1<<endl;
else
	cout<<"p2:"<<*p2<<endl;

为了能够转移所有权,提供了move()方法,称为移动语义,可以将p1的所有权转移给p2,此段代码输出为p2:100。

此时p1指向的空间已不能再操作,原因是p1已经是野指针。


shared_ptr

初始化方法

  • 方法一

std::shared_ptr<类型> p1(new 类型(值));

  • 方法二:

std::shared_ptr<类型> p1

p1.reset(new 类型(值));

  • 方法三

std::shared_ptr<类型> p1

p1 = make_shared<类型>(值);

  • 对于auto_ptr缺陷的解决方式
shared_ptr<int> p1;
p1.reset(new int(200));
shared_ptr<int> p2;
p2 = make_shared<int>(300);
p2 = p1;

cout<<"p1:"<<*p1<<endl;
cout<<"p2:"<<*p2<<endl;
cout<<"use_count:"<<p2.use_count()<<endl;

输出为

 

编译通过,正常输出,shared_ptr,顾名思义,多个shared_ptr可以共享同一块内存空间,通过引用计数来控制空间的回收,调用use_count()方法可以查看引用计数。

当有一个指针指向该空间时,引用计数+1,当引用计数归零时候,才回收内存空间。

  • 循环引用问题
#include<iostream>
#include<memory>
#include<string>
using namespace std;

class B;
class A
{
public:
	A()
	{
		cout<<"A()"<<endl;
	}
	~A()
	{
		cout<<"~A()"<<endl;
	}
public:
	shared_ptr<B> p1;
};

class B
{
public:
	B()
	{
		cout<<"B()"<<endl;
	}
	~B()
	{
		cout<<"~B()"<<endl;
	}
public:
	shared_ptr<A> p2;
};
int main()
{
	{
		shared_ptr<A> pA(new A);
		shared_ptr<B> pB(new B);
		pA->p1 = pB;
		pB->p2 = pA;
		cout<<"pA use_count:"<<pA.use_count()<<endl;
		cout<<"pB use_count:"<<pB.use_count()<<endl;
	}
	system("pause");
	return 0;
}

运行输出为

正常情况下,{}内定义的指针pA和pB在{}结束后将会被回收,但是由于产生了循环引用的问题,导致上述运行的意外情况。

图中,类A中定义的p1指向pB,类B中定义的p2指向pA,两块内存空间的引用计数都为2,在走出{}范围后,pA、pB的生命周期结束,各自的引用计数-1,但shared_ptr要在引用计数归零时才调用析构函数回收空间,而p1和p2间仍存在指向关系,所以引用计数各自为1,这就是循环引用的问题。


weak_ptr

初始化方法

  • 使用注意

weak_ptr只能通过已经初始化的shared_ptr进行初始化,且该模板类中没有重载*和->操作符,因此不能通过*解引用和通过->调用,只能在调用lock()方法获取使用权限后,再给shared_ptr或weak_ptr赋值。

std::shared_ptr<类型> p1(new 类型(值));

std::weak_ptr<类型> p2(p1); (其类型要与shared_ptr相同

shared_ptr<int> p1(new int(100));
weak_ptr<int> p2(p1);
p2.lock();
shared_ptr<int> p3(p2);
weak_ptr<int> p4(p2);
cout<<*p3<<endl;
shared_ptr<int> p5 = p4.lock();
cout<<*p5<<endl;

输出结果


通过上述代码可以看出:

weak_ptr在调用lock()方法后,获得一个所指向的shared_ptr的实例,可以给其他shared_ptr或weak_ptr进行初始化和赋值。 

shared_ptr<int> p1(new int(100));
weak_ptr<int> p2(p1);
p2.lock();
p1.reset();
if(p2.expired())
	cout<<"p2 is delete"<<endl;
else
	shared_ptr<int> p3(p2);

在p1调用reset()方法,weak_ptr指向的shared_ptr的空间被回收后, p2也被回收,通过调用expired()方法可以判断p2是否指向空,上述代码执行结果为输出p2 is delete。

weak_ptr<int> p1;
shared_ptr<int> p2(p1);

在vs2012下,这2行代码可以通过编译,但会崩溃,切忌出现这种操作。

  • 解决循环引用的问题

引入weak_ptr解决shared_ptr循环引用的问题,shared_ptr与weak_ptr在指向同一块空间时,各自维护自身的引用计数。

在释放时,pA生命周期先结束,它的引用计数pA use_count从1变为0,调用析构函数回收该空间,因此图中4代表的指针也被回收;

此时pB use_count在指向该空间的指针4被回收后,由2变为1,pB的生命周期随后结束,释放自身空间,引用计数从1变为0,pB指向的空间也调用析构函数回收,进而解决了循环引用的问题。

智能指针的实现,请参考:

https://blog.csdn.net/caoshangpa/article/details/79221544

猜你喜欢

转载自blog.csdn.net/qq_37348221/article/details/113314106