菜鸟笔记
提升您的技术认知

c++中的智能指针

简介

  为了解决c++程序容易造成内存泄漏的问题,c++11提供了3种智能指针:std::unique_prt, std::shared_ptr, std::weak_ptr。智能指针的原理就是,将一个申请好的内存地址保存在智能指针结构体内部,然后把智能指针保存在栈上。当智能指针出了作用域后,由于栈上的变量会自动销毁,所以之前传入保存在智能指针内部的内存块的析构函数也会被相应调用。智能指针和普通指针最好不要一块使用,否则很容易造成使用同一个对象初始化了多个智能指针而导致对象被多次析构的情况。只有第一个智能指针使用普通指针来初始化,后续和此普通指针相关的智能指针都应该使用智能指针来初始化。

  使用的时候需要include, 并且编译应该加上 -std=c++0x。

unique_ptr

  unique_ptr是独享所有权的指针。只要是按照正确的方法使用,不会出现同一个指针共享某一个对象这样的情况。使用unique_ptr的时候应该注意以下事项:

    <1> unique_ptr 无法进行拷贝构造,赋值的操作,只能通过move函数进行所有权的转换。

    <2> unique_ptr 可以直接与NULL进行比较,从而判断智能指针指向的对象是否为空

    <3> unique_ptr 使用release函数只是放弃所有权,对象并不会被析构。

    <4> 由于unique_ptr 不支持拷贝,所以不能成为容器对象。

c++中的智能指针

输出
c++中的智能指针

shared_ptr

  shared_ptr和unique_ptr的区别在于,同时可以有多个shared_ptr指向一个对象。shared_ptr使用引用计数来保存当前有多少个智能指针在引用这个对象。当引用计数降为0时,对象会被销毁。

  使用shared_ptr应该注意以下问题:

    <1> shared_ptr不光可以传入new出的内存来构造,还可以传入unique_ptr, weak_ptr来构造

    <2> 可以使用成员函数use_count来获得对象的引用计数。

    <3> 可以直接与NULL进行比较,从而判断智能指针指向的对象是否为空

    <4> shared_ptr内部的引用计数虽然是线程安全的,但是使用shared_ptr指针读取数据时,仍然需要加锁。

    <4> 注意环形引用的问题
c++中的智能指针

输出
c++中的智能指针

weak_ptr

  weak_ptr主要是用来辅佐shared_ptr正常运行而提出来的,主要用于解决shared_ptr环形引用问题。使用它来引用对象,不会增加对象的引用计数。shared_ptr可以直接赋值给weak_ptr,weak_ptr可以使用locl函数来获得shared_ptr。

  shared_ptr环形引用:比如有两个对象A和B,在A中通过shared_ptr引用B,在B中通过shard_ptr引用A,同时A和B本身也使用shared_ptr引用。那么A和B的引用计数将永远也不能降到0,A和B也永远不会被释放,这样就形成了死锁。比如如下代码,A和B形成了死锁,最后谁也没有释放:
c++中的智能指针
输出
c++中的智能指针

enable_shared_from_this的使用

  总结起来就一句话,enable_shared_from_this是用来让类对象在自己成员函数内部获取自己shared_ptr指针的。比如如下代码,我想在fun函数中获取自己的shared_ptr指针:

输出
c++中的智能指针

可以看到对象被析构了2次。在fun成员函数中,使用this指针初始化了shared_ptr p,这个p指针在出了fun函数后,被析构一次。因为tmp对象本身就是通过shared_ptr维护的,main函数结束时又被析构一次。这个问题的根源就在于,使用this指针初始化了2个智能指针,这个问题在简介中也强调过了。

  如果把代码改成这样,继承与enable_shared_from_this, 然后使用share_from_this来得到自己的shared_ptr,这个问题就不存在了。
c++中的智能指针
输出
c++中的智能指针