C++的智能指针

每日面经–10.14

C++的智能指针
想要弄清楚智能指针就要明白它为什么会产生,动态管理、动态数组、动态内存分配等都需要在堆上申请一块内存去保存自己的东西。你可能会觉得你自己用不到,我随便举一个栗子:比如说你下午要去买水果,但是不知道自己想买多少水果,但是由于现在必须出门,所以你要带多大的口袋是不知道的。在C++中你知道要保存一些数据到一个数组,你又想刚好数组的大小是你这些数据的大小。所以你就可以申请一个动态数组,当前面计算好了数据的大小,你这边直接赋值,比如下列程序:
在这里插入图片描述
如果p不申请为动态数组的话,那么会出现以下错误:
在这里插入图片描述
若为静态数组的话,这个n必须是常数。请注意申请了动态数组后需要用delete删除,所以delete和new是一对操作,delete[]和new[]他两都是一对cp出现,不删除的话就会有内存泄露。智能指针的作用就是它自己有delete包含,到了函数结束的时候,它自动调用自己的析构函数。

智能指针,C++11有这三个:auto_ptr,unique_ptr,shared_ptr。
砸门一个一个给大家介绍:
auto_ptr这个智能指针是最先出现的,其中有一个缺点有点难受,就是一个auto_ptr1赋值给另一个auto_ptr2后,auto_ptr1会直接没了,之后是不能对auto_ptr1进行操作的。如下图

// An highlighted block
auto_ptr<Apple> apple0(new Apple(1));
auto_ptr<Apple> apple1 = apple0;

apple0.id();  // 引发异常

unique_ptr既然前面的auto_ptr容易丢弃以往的数据,那么这个智能指针就直接是不许赋值构造和复制构造,如下图

unique_ptr<Apple> apple = new Apple(2); // 错误
unique_ptr<Apple> apple(new Apple(2));  // 正确

int main() 
{
    {
        unique_ptr<Apple> apple2(new Apple(2));
        unique_ptr<Apple> apple3 = apple2; // 编译失败
    } // 超出apple2的作用域,apple2被销毁,其指针指向的对象被析构。
}

虽然不能实现赋值,但是可以将所有权给别的智能指针,如下图

int main() 
{
    {
        unique_ptr<Apple> apple2(new Apple(2));

        unique_ptr<Apple> apple3;
        apple2.swap(apple3);     // apple2的指针从此无效

        /*
        或者:
        unique_ptr<Apple> apple3 = std::move(apple2);  // apple2指针从此无效
        */
    } // 超出apple2的作用域,apple2被销毁,其指针指向的对象被析构。
}

以上这两个智能指针都不能进行用**=进行赋值,但是shared_ptr**指针却可以,如下图所示:

#include <iostream>
#include <memory>
using namespace std;
class A
{
public:
    int i;
    A(int n):i(n) { };
    ~A() { cout << i << " " << "destructed" << endl; }
};
int main()
{
    shared_ptr<A> sp1(new A(2)); //A(2)由sp1托管,
    shared_ptr<A> sp2(sp1);       //A(2)同时交由sp2托管
    shared_ptr<A> sp3;
    sp3 = sp2;   //A(2)同时交由sp3托管
    cout << sp1->i << "," << sp2->i <<"," << sp3->i << endl;
    A * p = sp3.get();      // get返回托管的指针,p 指向 A(2)
    cout << p->i << endl;  //输出 2
    sp1.reset(new A(3));    // reset导致托管新的指针, 此时sp1托管A(3)
    sp2.reset(new A(4));    // sp2托管A(4)
    cout << sp1->i << endl; //输出 3
    sp3.reset(new A(5));    // sp3托管A(5),A(2)无人托管,被delete
    cout << "end" << endl;
    return 0;
}

程序的输出结果如下:
2,2,2
2
3
2 destructed
end
5 destructed
4 destructed
3 destructed

可以用第 14 行及第 16 行的形式让多个 sharecLptr 对象托管同一个指针。这多个 shared_ptr 对象会共享一个对共同托管的指针的“托管计数”。有 n 个 shared_ptr 对象托管同一个指针 p,则 p 的托管计数就是 n。当一个指针的托管计数减为 0 时,该指针会被释放。shared_ptr 对象消亡或托管了新的指针,都会导致其原托管指针的托管计数减 1。

第 20、21 行,shared_ptr 的 reset 成员函数可以使得对象解除对原托管指针的托管(如果有的话),并托管新的指针。原指针的托管计数会减 1。

输出的第 4 行说明,用 new 创建的动态对象 A(2) 被释放了。程序中没有写 delete 语句,而 A(2) 被释放,是因为程序的第 23 行执行后,已经没有 shared_ptr 对象托管 A(2),于是 A(2) 的托管计数变为 0。最后一个解除对 A(2) 托管的 shared_ptr 对象会释放 A(2)。

main 函数结束时,sp1、sp2、sp3 对象消亡,各自将其托管的指针的托管计数减为 0,并且释放其托管的指针,于是会有以下输出:
5 destructed
4 destructed
3 destructed

只有指向动态分配的对象的指针才能交给 shared_ptr 对象托管。将指向普通局部变量、全局变量的指针交给 shared_ptr 托管,编译时不会有问题,但程序运行时会出错,因为不能析构一个并没有指向动态分配的内存空间的指针。
注意,不能用下面的方式使得两个 shared_ptr 对象托管同一个指针:

A* p = new A(10);
shared_ptr <A> sp1(p), sp2(p);

sp1 和 sp2 并不会共享同一个对 p 的托管计数,而是各自将对 p 的托管计数都记为 1(sp2 无法知道 p 已经被 sp1 托管过)。这样,当 sp1 消亡时要析构 p,sp2 消亡时要再次析构 p,这会导致程序崩溃。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

万小猿

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值