C++学习 三、weak_ptr使用

本文详细介绍了C++中weak_ptr的使用方法及其与shared_ptr的区别。探讨了weak_ptr的创建方式、成员函数及如何利用它解决shared_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++智能指针的总结。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值