智能指针循环引用问题】

本文详细介绍了C++11中的三种智能指针:shared_ptr、unique_ptr和weak_ptr,它们如何避免内存泄露,特别是针对循环引用问题的解决方案。通过实例演示,展示了如何使用weak_ptr解决共享智能指针间的循环引用,确保正确释放内存。

1.智能指针

在 C/C++ 语言中,内存泄露的问题一直困扰着广大的开发者,因此各类库和工具的一直在努力尝试各种方法去检测和避免内存泄露,如 boost,智能指针技术应运而生。c++11中引入了智能指针,智能指针是存储指向动态分配对象指针的类,用于生存期的控制,能够确保在离开指针所在作用域时,自动的销毁分配的对象,防止内存泄漏。智能指针的核心实现技术为引用技术,每使用它一次内部引用计数+1,析构一次引用计数则-1,当引用计数减为0的时候则删除原始指针指向的堆区空间。简要的说,智能指针利用了 C++ 的 RAII 机制,在智能指针对象作用域结束后,会自动做内存释放的相关操作,不需要我们再手动去操作内存。使用智能指针时要引用头文件<memory>.

2.智能指针的类型

  • shared_ptr(共享智能指针)

shared_ptr实现共享式拥有概念。多个智能指针可以指向相同对象,该对象和其相关资源会在“最后一个引用被销毁”时候释放。从名字share就可以看出了资源可以被多个指针共享,它使用计数机制来表明资源被几个指针共享。可以通过成员函数use_count()来查看资源的所有者个数。如果有N个shared_ptr指向同一块内存,那么该内存引用计数+N;

        shared_ptr<int>ptr1(new int(100));
	cout << "ptr1管理的内存引用计数:" << ptr1.use_count() << endl;
	shared_ptr<int>ptr2(ptr1);
	cout << "ptr2管理的内存引用计数:" << ptr2.use_count() << endl;
	shared_ptr<int>ptr3(ptr1);
	cout << "ptr3管理的内存引用计数:" << ptr3.use_count() << endl;
	shared_ptr<int>ptr4(ptr1);
	cout << "ptr4管理的内存引用计数:" << ptr4.use_count() << endl;

  • unique_ptr(独占智能指针)

unique_ptr实现独占式拥有或严格拥有概念,保证同一时间内只有一个智能指针可以指向该对象,可以通过构造函数初始化一个独占智能指针,但是不允许通过赋值将一个unique_ptr赋值给另一个unique_ptr。它对于避免资源泄露(例如“以new创建对象后因为发生异常而忘记调用delete”)特别有效。

        unique_ptr<string> p3(new string("asfogjros"));
	unique_ptr<string> p4;
	p4 = p3;        // 此时会报错
  • weak_ptr(弱引用智能指针)

weak_ptr 是一种不控制对象生命周期的智能指针, 它可以看作是shared_ptr的助手,指向一个 shared_ptr 管理的对象,它没有重载操作符*和->,不能操作资源.,只是提供了对管理对象的一个访问手段。weak_ptr 设计的目的是为配合 shared_ptr 而引入的一种智能指针来协助 shared_ptr 工作, 它只可以从一个 shared_ptr 或另一个 weak_ptr 对象构造, 它的构造和析构不会引起引用记数的增加或减少。

        shared_ptr<int>ptr1(new int(100));
	cout << "ptr1管理的内存引用计数:" << ptr1.use_count() << endl;
	weak_ptr<int>ptr2(ptr1);
	cout << "ptr2管理的内存引用计数:" << ptr2.use_count() << endl;
	weak_ptr<int>ptr3(ptr1);
	cout << "ptr3管理的内存引用计数:" << ptr3.use_count() << endl;
	weak_ptr<int>ptr4(ptr1);
	cout << "ptr4管理的内存引用计数:" << ptr4.use_count() << endl;

3.循环引用问题

一段代码为例

#include<iostream>
#include<memory>
#include<string>
using namespace std;
class A;
class B;
class A {
public:
	shared_ptr<B> bptr;
	~A()
	{
		cout << "class Ta is disstruct" << endl;
	}
};
class B {
public:
	shared_ptr<A>aptr;
	~B()
	{
		cout << "class Tb is disstruct" << endl;
	}
};
void testPtr()
{
	shared_ptr<A>ap(new A);
	shared_ptr<B>bp(new B);
	cout << "ap的引用计数" << ap.use_count() << endl;//ap的引用计数1
	cout << "bp的引用计数" << bp.use_count() << endl;//bp的引用计数1
	ap->bptr = bp;
	bp->aptr = ap;
	cout << "ap的引用计数" << ap.use_count() << endl;//ap的引用计数2


	cout << "bp的引用计数" << bp.use_count() << endl;//bp的引用计数2
}
int main()
{
	testPtr();
	return 0;
}

我们可以用图来理解一下上述程序智能指针引用关系:

        共享智能指针ap指向A的实例对象,内存引用计数+1,B的实例对象里面的成员aptr被ap赋值,所以aptr与ap共同指向同一块内存,该内存引用计数变为2;同理指向B对象的也有两个共享智能指针,其引用计数也为2。

        当函数结束时,ap,bp两个共享智能指针离开作用域,引用计数均减为1,在这种情况下不会删除智能指针所管理的内存,导致A,B的实例对象不能被析构,最终造成内存泄漏,如图:

 4.解决方案       

        由于weak_ptr不管理share_ptr内部指针,它没有重载操作符*和->,不能操作资源.,构造和析构不会引起引用记数的增加或减少,所以我们可以通过使用weak_ptr解决循环引用问题,只需要将类A或者类B的任意一个成员改为weak_ptr类型(代码如下,以改变类A为例):

#include<iostream>
#include<memory>
#include<string>
using namespace std;
class A;
class B;
class A {
public:
	weak_ptr<B> bptr;
	~A()
	{
		cout << "class Ta is disstruct" << endl;
	}
};
class B {
public:
	shared_ptr<A>aptr;
	~B()
	{
		cout << "class Tb is disstruct" << endl;
	}
};
void testPtr()
{
	shared_ptr<A>ap(new A);
	shared_ptr<B>bp(new B);
	cout << "ap的引用计数" << ap.use_count() << endl;//ap的引用计数1
	cout << "bp的引用计数" << bp.use_count() << endl;//bp的引用计数1
	ap->bptr = bp;
	bp->aptr = ap;
	cout << "ap的引用计数" << ap.use_count() << endl;//ap的引用计数2
	cout << "bp的引用计数" << bp.use_count() << endl;//bp的引用计数1
        //class Tb is disstruct
        //class Ta is disstruct
}
int main()
{
	testPtr();
	return 0;
}

        上述程序中,在对类A成员赋值ap->bptr=bp时,由于bptr是weak_ptr类型,这个赋值操作不会引起引用计数的增加,此时bp的引用计数仍然为1,如图:

        当bp离开作用域之后,bp的引用计数变为0,类B的实例对象便被析构,同时其内部的成员aptr也被析构,aptr对A对象的管理也就随之解除,A对象内存引用计数变为1,当共享智能指针ap离开作用域后,其对A对象的管理解除,因此A内存引用计数变为0,类A也就被析构,函数结束随之出现类A和类B的析构函数,从而解决了循环引用问题。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值