C++智能指针(二)shared_ptr和weak_ptr

shared_ptr

共享指针的最显著特点就是计数器。

	shared_ptr<string> p0;
	cout << p0.use_count() << endl;
	shared_ptr<string> p1(new string("hello"));
	cout << p1.use_count() << endl;
	shared_ptr<string> p2 = p1;
	cout << p2.use_count() << endl;
	system("pause");
	return 0;

共享指针内部有一个use_count函数,能够返回该对象被引用的次数。

0
1
2
请按任意键继续. . .

shared_ptr允许多个指针指向同一个对象。
那么引用计数有啥用呢?

1,一旦一个对象通过调用new被分配出来,记录谁拥有这个对象是很重要的,因为其所有者要负责对它进行delete。但是对象所有者可以有多个,且所有权能够被传递,这就使得内存跟踪变得困难。引用计数可以跟踪对象所有权,并能够自动销毁对象

2,节省内存,提高程序运行效率。如果很多对象有相同的值,为这多个相同的值存储多个副本是很浪费空间的,所以最好做法是让左右对象都共享同一个值的实现。

shared_ptr的初始化方法

	int *p = new int(3);
	shared_ptr<int> sptr(p);//引用计数,1
	shared_ptr<int> sptr2(new int(4));//引用计数,1
	shared_ptr<int> sptr3 = sptr2;//拷贝构造,引用计数,2
	shared_ptr<int> sptr4 = make_shared<int>(5);//引用计数,1

1,用指针初始化(不推荐)
2,用new+类型(初始值)初始化
3,拷贝构造初始化(引用计数会+1)
4,用make_shared<类型>(值)初始化

为什么1这个方法不推荐呢?因为容易出错

int main()
{
	//foo();
	int* p = new int(1);
	shared_ptr<int> sptr(p);
	cout << sptr.use_count() << endl;
	{
		shared_ptr<int> sptr2(p);
		cout << sptr2.use_count() << endl;
	}
	system("pause");
	return 0;
}

输出的两个use_count都是1,说明共享指针没有增加计数,也就是说,第二个共享指针不知道第一个共享指针的存在,如果第二个共享指针被析构了,那么第一个共享指针就会出问题。

weak_ptr

这个例子转自:https://zhuanlan.zhihu.com/p/73807983
为什么需要weak_ptr?是为了避免循环引用的问题。weak_ptr在赋值的时候,不会引起计数器增加。weak_ptr被设计为与shared_ptr共同工作,可以从一个shared_ptr或者另一个weak_ptr对象构造,获得资源的观测权,而在weak_ptr析构的时候也不会导致计数器减少。
如果要操作资源,则必须使用一个成员函数 lock(),从被观测的shared_ptr获得一个可用的shared_ptr对象,从而操作资源。
那么什么是循环引用呢?

class TestB;
class TestA {
public:
	TestA() { cout << "TestA()构造函数" << endl; }
	void ReferTestB(shared_ptr<TestB> test_ptr)
	{
		m_TestB_ptr = test_ptr;
	}
	~TestA() { cout << "TestA()析构函数" << endl; }
private:
	shared_ptr<TestB> m_TestB_ptr;
};
class TestB
{
public:
	TestB() { cout << "TestB()构造函数" << endl; }
	void ReferTestA(shared_ptr<TestA> test_ptr) {
		m_TestA_ptr = test_ptr;
	}
	~TestB() { cout << "TestB()析构函数" << endl; }
private:
	shared_ptr<TestA> m_TestA_ptr;
};
int main()
{
	shared_ptr<TestA> a = make_shared<TestA>();
	shared_ptr<TestB> b = make_shared<TestB>();
	a->ReferTestB(b);
	b->ReferTestA(a);
	system("pause");
	return 0;
}

运行结果是:只显示了构造函数,没有析构函数。
因此可以看出,在这段代码中,析构函数没有正常的被执行,发生了内存泄漏。
原因: a引用了b,b引用了a,在main退出之前,a和b的引用计数都是2,退出main函数吼,引用计数变成了1,也就是互相引用,最后计数器不会归0,析构函数也就不会被调用了。

这时候可以使用弱指针对上述代码进行修改:

#include <iostream>
#include <memory>
class TestB;
class TestA
{
public:
    TestA()
    {
     std::cout << "TestA()" << std::endl;
    }
    void ReferTestB(std::shared_ptr<TestB> test_ptr)
    {
     m_TestB_Ptr = test_ptr;
    }
    void TestWork()
    {
     std::cout << "~TestA::TestWork()" << std::endl;
    }
    ~TestA()
    {
     std::cout << "~TestA()" << std::endl;
    }
private:
    std::weak_ptr<TestB> m_TestB_Ptr;
};
class TestB
{
public:
    TestB()
    {
     std::cout << "TestB()" << std::endl;
    }
    void ReferTestB(std::shared_ptr<TestA> test_ptr)
    {
     m_TestA_Ptr = test_ptr;
    }
    void TestWork()
    {
     std::cout << "~TestB::TestWork()" << std::endl;
    }
    ~TestB()
    {
        把std::weak_ptr类型转换成std::shared_ptr类型
     std::shared_ptr<TestA> tmp = m_TestA_Ptr.lock();
     tmp->TestWork();
     std::cout << "2 ref a:" << tmp.use_count() <<std::endl;
     std::cout << "~TestB()" << std::endl;
    }
    std::weak_ptr<TestA> m_TestA_Ptr;
};
int main()
{
  std::shared_ptr<TestA> ptr_a = std::make_shared<TestA>();
  std::shared_ptr<TestB> ptr_b = std::make_shared<TestB>();
  ptr_a->ReferTestB(ptr_b);
  ptr_b->ReferTestB(ptr_a);
  std::cout << "1 ref a:" << ptr_a.use_count()<<std::endl;
  std::cout << "1 ref b:" << ptr_a.use_count()<<std::endl;
    return 0;
}

运行结果:

TestA()
TestB()
1 ref a:1
1 ref b:1
~TestA::TestWork()
2 ref a:2
~TestB()
~TestA()

可以看到,weak_ptr在使用lock()函数后可以转化成shared_ptr,也就拥有了控制权。例子中,在析构函数~TestB中,声明一个shared_ptr的tmp,从A类的弱指针转化为共享指针,并调用A中的TestWork方法。

weak_ptr支持的调用

weak_ptr<T> w;  //空weak_ptr可以指向类型为T的对象
weak_ptr<T> w(shared_ptr sp);   //与sp指向相同对象的weak_ptr, T必须能转换为sp指向的类型
w = p;  //p可以是shared_ptr或者weak_ptr,赋值后w和p共享对象
w.reset();  //weak_ptr置为空
w.use_count();  //与w共享对象的shared_ptr的计数
w.expired();    //w.use_count()为0则返回true,否则返回false
w.lock();   //w.expired()为true,返回空的shared_ptr;否则返回指向w的shared_ptr
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值