C++11的智能指针

       c++11摒弃了auto_ptr,支持 shared_ptr,unique_ptr,weak_ptr,包含在头文件<memory>中。

       智能指针是利用了一种叫做RAII(资源获取即初始化)的技术对普通的指针进行封装,用是防止忘记调用delete释放内存和程序异常的进入catch块忘记释放内存。当超出了智能指针类的作用域是,类会自动调用析构函数,析构函数会自动释放资源。

shared_ptr

      shared_ptr多个指针指向相同的对象。shared_ptr使用引用计数,每一个shared_ptr的拷贝都指向相同的内存。每建一个shared_ptr,内部的引用计数加1,每析构一次,内部的引用计数减1,减为0时,自动删除所指向的堆内存。shared_ptr内部的引用计数是线程安全的,但是对象的读取需要加锁。

  • 初始化。智能指针是个模板类,可以指定类型,传入指针通过构造函数初始化。也可以使用make_shared函数初始化。不能将指针直接赋值给一个智能指针,一个是类,一个是指针。例如std::shared_ptr<int> p4 = new int(1);的写法是错误的
  • 拷贝和赋值。拷贝使得对象的引用计数增加1,赋值使得原对象引用计数减1,当计数为0时,自动释放内存。后来指向的对象引用计数加1,指向后来的对象。
  • get函数获取原始指针,或者直接使用*或->使用原始指针。
  • 注意不要用一个原始指针初始化多个shared_ptr,否则会造成二次释放同一内存。
  • 注意避免循环引用,shared_ptr的一个最大的陷阱是循环引用,循环,循环引用会导致堆内存无法正确释放,导致内存泄漏。循环引用可以使用weak_ptr避免。
#include <memory>
#include <iostream>
using namespace std;
int main()
{
    int a = 5;
    //shared_ptr<int> sp1(new int(5));
    shared_ptr<int> sp1 = make_shared<int>(a);
    cout<<"*sp1 = "<<*sp1<<endl;
    cout<<"use count = "<<sp1.use_count()<<endl;

    {
        shared_ptr<int> sp2(sp1);
        cout<<"*sp2 = "<<*sp2.get()<<endl;
        cout<<"use count = "<<sp1.use_count()<<endl;
    }

    cout<<"use count = "<<sp1.use_count()<<endl;
    return 0;
}

输出

*sp1 = 5
use count = 1
*sp2 = 5
use count = 2
use count = 1

unique_ptr

       某个时刻只能有一个unique_ptr指向一个给定对象,由于一个unique_ptr拥有它指向的对象,因此unique_ptr不支持普通的拷贝或赋值操作。 不可以对unique_ptr进行拷贝、赋值等操作,但是可以通过调用release或reset将指针所有权从一个(非const)unique_ptr转移给另一个unique。当unique_ptr被销毁时,该对象被销毁。

       unique_ptr不像shared_ptr一样拥有标准库函数make_shared来创建一个shared_ptr实例。要想创建一个unique_ptr,我们需要将一个new 操作符返回的指针传递给unique_ptr的构造函数。

#include <memory>
#include <iostream>
using namespace std;
int main()
{
    unique_ptr<int> up1(new int(5));
    cout<<"*up1 = "<<*up1<<endl;
    cout<<"up1.get() = "<<up1.get()<<endl;

    unique_ptr<int> up2 = move(up1);
    cout<<"*up2 = "<<*up2<<endl;
    cout<<"up2.get() = "<<up2.get()<<endl;

    cout<<"up1.get() = "<<up1.get()<<endl;

    return 0;
}

输出

*up1 = 5
up1.get() = 0xf71530
*up2 = 5
up2.get() = 0xf71530
up1.get() = 0

weak_ptr

        weak_ptr是用来解决shared_ptr相互引用时的死锁问题,如果两个shared_ptr相互引用,那么这两个指针的引用计数永远不可能下降为0,资源永远不会释放。它是对对象的一种弱引用,不会增加对象的引用计数,和shared_ptr之间可以相互转化,shared_ptr可以直接赋值给它,因为可能出现weak_ptr所指向的对象实际上已经被释放了的情况,所以weak_ptr有一个lock函数,尝试取回一个指向对象的shared_ptr。

#include <memory>
#include <iostream>
using namespace std;
class CB;
class CA
{
public:
    shared_ptr<CB> pb_;
    ~CA()
    {
        cout<<"delete A"<<endl;
    }
};

class CB
{
public:
    shared_ptr<CA> pa_;
    ~CB()
    {
        cout<<"delete B"<<endl;
    }
};

int main()
{
    {
        shared_ptr<CA> spa(new CA());
        shared_ptr<CB> spb(new CB());
        spa->pb_ = spb;
        spb->pa_ = spa;
        cout<<spb.use_count()<<endl;
        cout<<spa.use_count()<<endl;
    }
    return 0;
}

得到输出结果:

2
2

      因为互相引用,两个资源的引用计数都为2,当超出作用范围时,智能指针spa,spb析构时两个资源引用计数会减一,但是两者引用计数还是为1,导致跳出函数时资源没有被释放(A B的析构函数没有被调用)。

       如果把其中一个改为weak_ptr,如把类B里面的shared_ptr<CA> pa_改为

weak_ptr<CA> pa_;

运行结果如下

2
1
delete A
delete B

A的引用开始就只有1,当spa析构时,A的计数变为0,A得到释放,A释放的同时也会使B的计数减一,同时spb析构时使B的计数减一,那么B的计数为0,B得到释放。如果是把A里面的改为weak_ptr,就会发现是B先析构。

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值