C++学习 三、weak_ptr使用
前言
本篇继续智能指针weak_ptr的学习。
weak_ptr虽然是智能指针,但实际上是作为shared_ptr的辅助指针使用。
weak_ptr概述
weak_ptr通常不单独使用,一般用于查看对应的shared_ptr的信息。weak_ptr没有重载*,->等指针运算符。
weak_ptr对象不会影响shared_ptr对象的引用计数。
创建weak_ptr对象
创建空weak_ptr对象
std::weak_ptr<double> w1;
//std::weak_ptr<double> w1(); // equal
注意:不能使用nullptr构造weak_ptr对象,这一点与shared_ptr,unique_ptr都不同!以下语句将报错:
//std::weak_ptr<double> w1(nullptr); // error!
//std::weak_ptr<double> w2 = nullptr; // error!
使用拷贝构造函数初始化weak_ptr对象
std::weak_ptr<double> w2(w1);
//std::weak_ptr<double> w2 = w1; // equal
使用shared_ptr对象初始化weak_ptr对象
由于weak_ptr是shared_ptr的辅助指针,因此可以通过shared_ptr对象初始化weak_ptr对象:
std::shared_ptr<double> s3(new int(6));
std::weak_ptr<double> w3(s3);
std::weak_ptr<double> w4 = s3;
注意:即使通过shared_ptr对象初始化weak_ptr对象,shared_ptr对象的引用计数也不会发生变化。
注意:不能使用堆指针初始化weak_ptr对象
shared_ptr,unique_ptr都可以通过堆指针来初始化对象,但weak_ptr不行。
//std::weak_ptr<double> w5(new double); //error!
weak_ptr成员函数
reset()使weak_ptr对象变为空指针,类似默认构造函数。(与shared_ptr,unique_ptr的reset不同,weak_ptr的reset不能带参数)。
swap(std::weak_ptr<T> &p)交换两个weak_ptr对象的内部指针。
use_count()查看关联shared_ptr的引用计数。
expired()判断weak_ptr是否过期。如果weak_ptr为空,或者关联的shared_ptr为空,返回true。
lock()如果weak_ptr对象已过期,返回一个空shared_ptr对象;否则,返回一个weak_ptr内部指针相关联的shared_ptr对象(因此,shared_ptr对象的引用计数+1)。
weak_ptr运算符
weak_ptr类只重载了=赋值运算符,可以把一个shared_ptr或weak_ptr对象赋给另一个weak_ptr对象。
w4 = w3;
使用weak_ptr解决shared_ptr循环引用问题
循环引用现象
shared_ptr的循环引用,是因为shared_ptr对象虽然被销毁,但其引用计数没有变为0,导致开辟的内存没有被释放:
class A;
class B;
class A
{
public:
std::shared_ptr<B> b_;
public:
A(){
std::cout << "construct A" << std::endl;
}
~A(){
std::cout << "destroy A" << std::endl;
}
};
class B
{
public:
std::shared_ptr<A> a_;
public:
B(){
std::cout << "construct B" << std::endl;
}
~B(){
std::cout << "destroy B" << std::endl;
}
};
// *** circular reference *** ///
std::shared_ptr<A> a(new A()); // a count: 1
std::shared_ptr<B> b(new B()); // b count: 1
a->b_ = b; // b count: 2
b->a_ = a; // a count: 2
对象a,b生命周期结束时,b先销毁,b count=1,a再销毁,a count=1,于是a,b内部指针指向的内存没有被释放,造成内存泄漏。
内存空间分析
细究一下上面代码的内存空间情况,首先通过shared_ptr a,b对象开辟了两个内存空间ma,mb以及对应的内部指针pa,pb,然后在pa指向的内存空间中通过b_存放了pb指针和空间mb,在pb指向的内存空间中通过a_存放了pa指针和空间ma,接着a,b对象分别被销毁,引用计数为1,因此指针pa,pb没有被销毁,内存空间ma,mb没有被释放。
可知,如果要解决循环引用的话,需要把其中一块内存空间手动释放,比如:
b->a_.reset();
或者,在智能指针赋值时只设置内部指针,不改变引用计数,也能解决循环引用。这就是weak_ptr的作用了。
weak_ptr解决循环引用
在类成员变量中使用weak_ptr,对象初始化时使用shared_ptr,就能够成功释放开辟的内存:
class A;
class B;
class A
{
public:
std::weak_ptr<B> b_;
public:
A(){
std::cout << "construct A" << std::endl;
}
~A(){
std::cout << "destroy A" << std::endl;
}
};
class B
{
public:
std::weak_ptr<A> a_;
public:
B(){
std::cout << "construct B" << std::endl;
}
~B(){
std::cout << "destroy B" << std::endl;
}
b->a_ = a,a->b_=b没有影响引用计数,因此a,b销毁时,a,b内部指针指向的内存空间也被正常释放了。
后记
下篇进行C++智能指针的总结。
本文详细介绍了C++中weak_ptr的使用方法及其与shared_ptr的区别。探讨了weak_ptr的创建方式、成员函数及如何利用它解决shared_ptr的循环引用问题。

5237

被折叠的 条评论
为什么被折叠?



