1.为什么需要weak_ptr
为了解决shared_ptr循环引用导致内存泄漏的问题,才引入了weak_ptr。通过引入weak_ptr,顺利解决了这个问题。
2.回顾shared_ptr循环引用问题
2.1代码及运行输出
struct TestB;
struct TestA{
TestA(){
a = QUuid::createUuid().toString();
qInfo() << a <<"已经被构造了"<<endl;
};
TestA(QString aa){
a = aa;
qInfo() << a <<"已经被构造了"<<endl;
}
~TestA(){
qInfo() << a <<"已经被析构了"<<endl;
}
QString a;
shared_ptr<TestB> b_ptr;
};
struct TestB{
TestB(){
b = QUuid::createUuid().toString();
qInfo() << b <<"已经被构造了"<<endl;
};
TestB(QString aa){
b = aa;
qInfo() << b <<"已经被构造了"<<endl;
}
~TestB(){
qInfo() << b <<"已经被析构了"<<endl;
}
QString b;
shared_ptr<TestA> a_ptr;
};
void Weak_ptr::whyNeedWeakPtr()
{
shared_ptr<TestA> a = make_shared<TestA>();
shared_ptr<TestB> b = make_shared<TestB>();
qInfo() << "a的引用计数为:"<<a.use_count()<<endl;
qInfo() << "b的引用计数为:"<<b.use_count()<<endl;
a->b_ptr = b;
b->a_ptr = a;
qInfo() << "a的引用计数为:"<<a.use_count()<<endl;
qInfo() << "b的引用计数为:"<<b.use_count()<<endl;
}
2.2原因分析
由输出结果可知,在循环引用之后,a,b,两个智能指针的引用计数都变成了2.在出了函数作用域之后,a,b指针的引用计数自动减一,也就是说都变成了1,但不会变为0,也就导致了这两个智能指针指向的对象没有被析构,导致了内存泄漏。
如果想解决这个问题,我们只要想个办法,在这种循环引用的情景下,其中任何一个的引用计数为1(就假比如说a)。这样的话,出函数作用域后,a智能指针的引用计数就变成0,指向对象就会被析构,析构的时候,对象内的b指针应用计数也会变为1.然后b指针也会出作用域,引用计数再次减一,指向的对象也就会正常析构了。
为了达到这个目的,大佬们引入了weak_ptr这个指针。
3.代码改造
我们只需要将上面的TestA中得shared_ptr改为weak_ptr就可以了,改造后的代码如下。
struct TestB;
struct TestA{
TestA(){
a = QUuid::createUuid().toString();
qInfo() << a <<"已经被构造了"<<endl;
};
TestA(QString aa){
a = aa;
qInfo() << a <<"已经被构造了"<<endl;
}
~TestA(){
qInfo() << a <<"已经被析构了"<<endl;
}
QString a;
weak_ptr<TestB> b_ptr;
};
然后再次执行之前的whyNeedWeakPtr()函数。输出结果如下
从输出图我们就可以看出和我们之前分析的一样,a中使用的weak_ptr,导致对应的引用计数没有加一,也就可以正常的析构了。
4.如何使用weak_ptr
void Weak_ptr::howToUse()
{
shared_ptr<Test> a = make_shared<Test>();
qInfo() << "a的引用计数为:" << a.use_count() ;
weak_ptr<Test> w = a;
//weak_ptr 的use_count()返回的时 与该weak_ptr共指向的shared_ptr的引用计数
qInfo() << "w的引用计数为:" << w.use_count() << "a的引用计数为:" << a.use_count() << endl;
shared_ptr<Test> b = w.lock(); //调用lock后如果 指向的内存没有被释放就返回共指向的shared_ptr,并使
//共指向的shared_ptr的引用计数加一,如果已经被释放就返回空
qInfo() << "w的引用计数为:" << w.use_count() << "a的引用计数为:" << a.use_count() << endl;
qInfo() << b->getString() << endl;
//weak_ptr 提供expired判断改指针是否已经失效,
qInfo() <<"w是否失效"<< w.expired();
b.reset();
a.reset();
qInfo() << "w是否失效" << w.expired();
shared_ptr<Test> c = make_shared<Test>();
weak_ptr<Test> w2 = c;
shared_ptr<Test> d = make_shared<Test>();
weak_ptr<Test> w3 = d;
qInfo() << "w2指向值为:" << w2.lock()->getString() << "w3指向值为:" << w3.lock()->getString();
w2.swap(w3); //交换共指向的shared_ptr
qInfo() << "w2指向值为:" << w2.lock()->getString() << "w3指向值为:" << w3.lock()->getString();
}
输出结果如下
5.使用weak_ptr注意事项
weak_ptr是一种弱引用,常常用来侦查对象是否存在,不控制对象的生命期,一般作为一个保护防止调用时共指向的shared_ptr已经失效,使用方法如下。
void Weak_ptr::checkIsValid()
{
shared_ptr<Test> a = make_shared<Test>();
qInfo() << "a指向值为:" << a->getString() << endl;
weak_ptr<Test> w(a);
if(!w.expired()){
qInfo()<< __LINE__ << w.lock()->getString() << endl;
}
a.reset();
qInfo() << w.expired() <<endl;
if(!w.expired()){
qInfo() << __LINE__ << w.lock()->getString() << endl;
}
}
输出如下
可以看出只执行了一次输出,在reset之后,w失效,下面的代码没有被执行,有效的进行了保护