shared_ptr和weak_ptr的区别与联系

头文件:#include <memory>

(1) 智能指针shared_ptr:

①不用手动释放对象占用的内存;

避免浅拷贝带来的内存重复释放问题。

③get函数用于返回指向的实际对象的指针,基本上等同于->操作符;

    std::shared_ptr<Abase> sp(new Abase());

    A* pa = sp.get();

    pa->print();   // 类中的方法

实现了*和->方法,因此可以操作实际对象。

⑤可使用use_count()来查看当前对象被引用的次数;

⑥可以使用make_shared创建shared_ptr对象,其中make_shared后面括号内的参数为选择要构造的实际对象的构造函数中的参数;

shared_ptr<Abase> A = make_shared<Abase>();
shared_ptr<Abase> A = make_shared<Abase>(“AAAAAA”);

(2) 弱引用指针weak_ptr:

①weak_ptr用于辅助shared_ptr的,只能指向shred_ptr对象,而不能直接指向实际对象本身,如

weak_ptr<Abase> ptr (new Abase());  // 编译器会报错;

②弱引用不能操作对象,因为没有实现operator* 和 operator->方法

③可以将shared_ptr对象或者另一个weak_ptr对象赋值给weak_ptr对象,但赋值后不会增加引用计数

④weak_ptr中只有函数lock和expired两个函数比较重要,因为它本身不会增加引用计数,所以它指向的对象可能在它用的时候已经被释放了,所以在用之前需要使用expired函数来检测是否过期,如果expired() == true(函数expired()的功能等价于use_count()==0),表示实际对象已经不复存在了,这个时候调用lock返回的是一个指向null的shared_ptr对象;如果expired() == false,可以使用lock函数来获取其对应的shared_ptr对象(lock()会创建shared_ptr指针,在该指针的作用域范围内,会使引用次数+1,出作用域,引用次数-1)。

⑤weak_ptr常用来解决循环引用带来的无法内存释放问题;

也可使用use_count()来查看当前对象的弱引用次数(没人会关心弱引用计数的次数,因为实际对象释放与否,是由智能指针引用次数决定的);

示例代码:

#include <iostream>
#include <memory>

using namespace std;

class base {
public:
    base(string name) : mName(name) {
        cout << "construct " << mName << endl;
    }
    virtual ~base() {
        cout << "deconstruct " << mName << endl;
    }
    void print() {
        cout << mName << " will print" << endl;
    }
public:
    string mName;
};

class Abase {
public:
    Abase(string name) : mName(name) {
        cout << "construct " << mName << endl;
    }
    virtual ~Abase() {
        cout << "deconstruct " << mName << endl;
    }
    void print() {
        cout << mName << " will print" << endl;
    }
public:
    string mName;
};

int main(int argc, char* argv[]) {
    {
        shared_ptr<Abase> A(new Abase("AAAAAAA"));
        // 或者用make_shared来构造
        // shared_ptr<Abase> A = maek_shared<Abase> ("AAAAAAA");
        shared_ptr<base> B(new base("BBBBBBB"));
        // 返回指向实际对象的普通指针
        Abase* ptr = A.get();
        ptr->print();
        cout << B.use_count() << endl;
        cout << A.use_count() << endl;
        weak_ptr<Abase> weaka = A;
        weak_ptr<base> weakb = B;
        {
            // 获取shared_ptr对象,这会使实际对象的引用次数+1,出作用域后引用计数-1
            shared_ptr<Abase> weakaLock = weaka.lock();
            weakaLock->print();
            cout << A.use_count() << endl;
        }
        if (!weakb.expired()) {
            // 将获取的shared_ptr对象赋值给临时变量,在这句话结束以后临时变量资源被回收,
            // 因此引用计数不会+1
            weakb.lock()->print();
            cout << B.use_count() << endl;
        }
        // 将weakb指向的对象清空
        weakb.reset();
        if (weakb.expired()) {
            cout << "weakb is invalid" << endl;
        }
        cout << B.use_count() << endl;
        cout << A.use_count() << endl;
    }
    system("pause");
    return 0;
}

日志输出:

construct AAAAAAA
construct BBBBBBB
AAAAAAA will print
1
1
AAAAAAA will print
2
BBBBBBB will print
1
weakb is invalid
1
1
deconstruct BBBBBBB
deconstruct AAAAAAA

接下来看看使用weak_ptr最多的场景,解决循环引用带来的内存泄漏问题,实例如下:

#include <iostream>
#include <memory>

using namespace std;
class Abase;
class base {
public:
    base(string name): mName(name) {
        cout << "construct " << mName << endl;
    }
    virtual ~base() {
        cout << "deconstruct " << mName << endl;
    }
    void print() {
        cout << mName << " will print" << endl;
    }
public:
    string mName;
    shared_ptr<Abase> base_ptr;
};

class Abase {
public:
    Abase(string name):mName(name){
        cout << "construct " << mName << endl;
    }
    virtual ~Abase() {
        cout << "deconstruct " << mName << endl;
    }
    void print() {
        cout << mName << " will print" << endl;
    }
public:
    string mName;
    shared_ptr<base> Abase_ptr;
};

int main(int argc, char* argv[]) {
    {
        shared_ptr<Abase> A(new Abase("AAAAAAA"));
        shared_ptr<base> B(new base("BBBBBBB"));
        cout << B.use_count() << endl;
        cout << A.use_count() << endl;
        A->Abase_ptr = B;
        B->base_ptr = A;
        cout << B.use_count() << endl;
        cout << A.use_count() << endl;
    }
    system("pause");
    return 0;
}

日志输出:

construct AAAAAAA
construct BBBBBBB
1
1
2
2

离开作用域后Abase,base对象的引用计数变为1,因此不会触发析构函数,这就导致了内存泄漏。因为weak_ptr不会增加引用计数,所以只要将Abase或base中的指针类型改成weak_ptr即可,此时日志输出:

construct AAAAAAA
construct BBBBBBB
1
1
1
2
deconstruct BBBBBBB
deconstruct AAAAAAA

再介绍下unique_ptr指针:

unique_ptr指针,是一个装指针的容器,且拥有关联指针的唯一所有权(内部没有引用计数)。当unique_ptr对象超出作用域时会自动析构,其析构函数中会delete其关联指针,这样就相当于替我们执行了delete堆内存上的对象。

创建unique_ptr对象有两种方法:

//C++11: 
std::unique_ptr<Abase> A_ptr(new Abase("AAAAAAA"));
//C++14: 
std::unique_ptr<Abase> A_ptr = std::make_unique<Abase>("AAAAAAA");

使用unique_ptr规则:

① unique_ptr不能直接复制,必须使用std::move()转移其管理的指针,转移后原 unique_ptr 为nullptr:

// 转移后 A_ptr2 == nullptr
std::unique_ptr<Abase> A_ptr1 = std::move(A_ptr2);

② 使用if(ptr1 == nullptr)来判断unique_ptr对象是否为空,也就是查看其是否有关联原始指针:

if(ptr1 == nullptr)

③通过get()获取关联指针;通过reset()重置unique_ptr对象为空;通过release()返回获取关联指针,并释放关联指针的所有权,此时unique_ptr对象为nullptr。

如有遗漏后续补充,bye~

  • 2
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值