只能指针的行为类似常规指针,重要的区别是它负责自动释放所指向的对象。智能指针定义在memory头文件中。

1. auto_ptr(C++11已经舍弃)

由new expression获得的对象,在auto_ptr对象销毁时,他所管理的对象也会自动被delete掉。

auto_ptr<string> p1(new string("This is a string"));
auto_ptr<string> p2;
p2 = p1;

上述语句中,如果p1和p2是常规指针,则两个指针将指向同一个string对象。这是不能接受的,因为程序将试图删除同一个对象两次。

要避免这种问题,方法有多种:

  1. 定义赋值运算符,使之执行深拷贝。这样两个指针将指向不同的对象,其中一个对象是另一个对象的副本,缺点是浪费空间,所以智能指针都未采用此方案。
  2. 建立所有权概念。对于特定的对象,只能有一个指针可拥有,这样所拥有对象的智能指针的析构函数会删除该对象。然后让赋值操作转让所有权。这就是用于auto_ptr和unique_ptr的策略。
  3. 跟踪引用对象的智能指针数。这称为引用计数。例如:赋值时,计数加1,指针过期时,计数减1。当减为0时才调用delete。这就是sheared_ptr采用的策略。

为什么要弃用auto_ptr?

#include<memory>
#include<iostream>
using namespace std;
int main()
{
auto_ptr<string> p1(new string("This is a string"));
auto_ptr<string> p2 = p1; //p1将所有权转让给p2,此时p1不再引用该字符串从而变成空指针。
cout << *p1 << endl; //报错,此时p1已经没有所指向的内存的所有权。
cout << *p2 << endl;
}
  1. 使用shared_ptr时运行正常,因为shared_ptr采用引用计数,p1和p2都指向同一块内存,在释放空间时因为事先要判断引用计数值的大小因此不会出现多次删除一个对象的错误。
  2. 使用unique_ptr时编译出错,与auto_ptr不同的是,使用unique_ptr时,程序不会等到运行阶段崩溃,而在编译时出现错误。

舍弃auto_ptr的原因:避免因潜在的内存问题导致程序崩溃

2. unique_ptr(替换auto_ptr)

unique_ptr比auto_ptr更加安全,因为auto_ptr有拷贝语义,拷贝后原对象变得无效,再次访问原对象时会导致程序崩溃;unique_ptr禁止了拷贝语义,但提供了移动语义,即可以使用move()进行控制权限的转移。

unique_ptr<string> p1(new string("This is a string"));
unique_ptr<string> p2(p1); //编译出错,已禁止拷贝
unique_ptr<string> p3 = p1; //编译出错,已禁止拷贝
unique_ptr<string> p4 = std :: move(p1); //控制权限转移 auto_ptr<string> p1(new string("This is a string"));
auto_ptr<string> p2(p1); //编译通过,运行出错
auto_ptr<string> p3 = p1; //编译通过,运行出错

如果unique_ptr是个临时右值,编译器允许拷贝语义。

unique_ptr<string> demo(string *s)
{
unique_ptr<string> temp(new string(s));
return temp;
} unique_ptr<string> ps;
ps = demo("This is a string");

demo()返回一个临时unique_ptr,然后ps接管了临时对象所管理的资源,而返回时临时的unique_ptr被销毁,也就是说没有机会使用unique_ptr来访问无效的数据。相对于auto_ptr任何情况下都允许拷贝语义,这正是unique_ptr更加灵活的地方。

扩展auto_ptr不能完成的功能:

  1. unique_ptr可放在容器中,弥补了auto_ptr不能作为容器元素的缺点。
  2. 管理动态数组
  3. 自定义资源删除操作。

3. shared_ptr(引用计数型智能指针)

类似vector,智能指针也是模板,当我们创建一个智能指针时,必须提供额外的信息---指针可以指向的类型。

shared_ptr<string> p1;

默认初始化的智能指针中保存着一个空指针。

智能指针的使用方式与普通指针类似。解引用一个智能指针返回它指向的对象。

make_shared函数

最安全的分配和使用动态内存的方法是调用一个名为make_shared的标准库函数。此函数在动态内存中分配一个对象并初始化它,返回指向此对象的shared_ptr。

shared_ptr<int> p1 = make_shared<int>(42);
shared_ptr<string> p2 = make_shared<string>(10, '9');
auto p3 = make_shared<vector<string>>();

shared_ptr的拷贝和赋值

  1. 当进行拷贝或赋值操作时,每个shared_ptr都会记录有多少个其他shared_ptr指向相同的对象。我们可以认为每个shared_ptr都有一个关联的计数器,通常称其为引用计数。
  2. 一旦一个shared_ptr的计数器变为0,它就会自动释放自己所管理的对象。

shared_ptr自动销毁所管理的对象

  1. 它是通过另一个特殊的成员函数---析构函数(destructor)完成销毁工作的。类似于构造函数,每个类都有一个析构函数。就像构造函数控制初始化一样,析构函数控制该类型的对象销毁时做什么操作。
  2. 析构函数一般用来释放对象所分配的资源。
  3. shared_ptr的析构函数会递减它所指向的对象的引用计数。如果引用计数变为0,shared_ptr的析构函数就会销毁对象,并释放它占用的内存。

shared_ptr还会释放相关联的内存

  1. shared_ptr在无用之后仍然保留的一种可能情况是,你将shared_ptr存放在一个容器中,随后重排了容器,从而不再需要某些元素。在这种情况下,你应该确保使用erase删除哪些不再需要的shared_ptr元素。

使用动态生存期的资源的类

  1. 程序不知道自己需要使用多少个对象(容器类)
  2. 程序不知道所需对象的准确类型
  3. 程序需要在多个对象间共享数据(如果两个对象共享底层的数据,当某个对象被销毁时,我们不能单方面的销毁底层数据。)

4. weak_ptr(辅助shared_ptr)

  1. weak_ptr是一种不控制所指向对象生存期的智能指针,它指向由一个shared_ptr管理的对象。将一个weak_ptr绑定到一个shared_ptr不会改变shared_ptr的引用计数。
  2. 创建一个weak_ptr时,要用一个shared_ptr来初始化它
  3. 不能使用weak_ptr直接访问对象,而必须调用lock。此函数检查weak_ptr指向的对象是否仍存在。如果存在,lock返回一个指向共享对象的shared_ptr.

5.shared_ptr循环引用

#include<iostream>
#include<memory>
using namespace std; class ListNode{
public:
int m_value;
shared_ptr<ListNode> prev;
shared_ptr<ListNode> next;
//构造函数
ListNode(int value):m_value(value){
cout << "constructor called!" <<endl;
}
//析构函数
~ListNode(){
cout << "destructor called!" <<endl;
}
}; void test(){
shared_ptr<ListNode> sp1 = make_shared<ListNode>(33);
shared_ptr<ListNode> sp2 = make_shared<ListNode>(44);
cout << sp1.use_count() << endl;
cout << sp2.use_count() << endl;
sp1 -> next = sp2;
sp2 -> prev = sp1;
cout << sp1.use_count() << endl;
cout << sp2.use_count() << endl;
} int main(){
test();
return 0;
}
//运行结果:
constructor called!
constructor called!
1
1
2
2

构造的sp1和sp2在出它们的作用域(即test())时,都没有被析构,从而造成了内存泄漏。

6.weak_ptr是如何解决循环引用的?

#include<iostream>
#include<memory>
using namespace std; class ListNode{
public:
int m_value;
weak_ptr<ListNode> prev;
weak_ptr<ListNode> next;
//构造函数
ListNode(int value):m_value(value){
cout << "constructor called!" <<endl;
}
//析构函数
~ListNode(){
cout << "destructor called!" <<endl;
}
}; void test(){
shared_ptr<ListNode> sp1 = make_shared<ListNode>(33);
shared_ptr<ListNode> sp2 = make_shared<ListNode>(44);
cout << sp1.use_count() << endl;
cout << sp2.use_count() << endl;
sp1 -> next = sp2;
sp2 -> prev = sp1;
cout << sp1.use_count() << endl;
cout << sp2.use_count() << endl;
} int main(){
test();
return 0;
}
//运行结果:
constructor called!
constructor called!
1
1
1
1
destructor called!
destructor called!

弱引用不修改对象的引用计数;弱引用能检测对象是否已经被释放。

只要把循环引用的一方使用弱引用,即可解除循环引用。

C++中的四个智能指针的更多相关文章

  1. c++中的四种智能指针

    c++中的四种智能指针 写惯了python,golang再来写c++总觉得头大,很大一个原因就是他没有一个GC机制. 不过c++中提供了智能指针,也不是不能用,李姐万岁! auto_ptr, shar ...

  2. 转载:STL四种智能指针

    转载至:https://blog.csdn.net/K346K346/article/details/81478223 STL一共给我们提供了四种智能指针: auto_ptr.unique_ptr.s ...

  3. stl中auto_ptr,unique_ptr,shared_ptr,weak_ptr四种智能指针使用总结

    stl中auto_ptr,unique_ptr,shared_ptr,weak_ptr四种智能指针使用总结 1. auto_ptrauto_ptr主要是用来解决资源自动释放的问题,比如如下代码:voi ...

  4. c++ 中的8种智能指针[转]

    一.简介 由于 C++ 语言没有自动内存回收机制,程序员每次 new 出来的内存都要手动 delete.程序员忘记 delete,流程太复杂,最终导致没有 delete,异常导致程序过早退出,没有执行 ...

  5. C++面试题(四)——智能指针的原理和实现

    C++面试题(一).(二)和(三)都搞定的话,恭喜你来到这里,这基本就是c++面试题的最后一波了.     1,你知道智能指针吗?智能指针的原理.     2,常用的智能指针.     3,智能指针的 ...

  6. 聊聊 C++ 中的几种智能指针 (下)

    一:背景 上一篇我们聊到了C++ 的 auto_ptr ,有朋友说已经在 C++ 17 中被弃用了,感谢朋友提醒,今天我们来聊一下 C++ 11 中引入的几个智能指针. unique_ptr shar ...

  7. 聊聊 C++ 中的几种智能指针 (上)

    一:背景 我们知道 C++ 是手工管理内存的分配和释放,对应的操作符就是 new/delete 和 new[] / delete[], 这给了程序员极大的自由度也给了我们极高的门槛,弄不好就得内存泄露 ...

  8. STL 智能指针

    转自: https://blog.csdn.net/k346k346/article/details/81478223 STL一共给我们提供了四种智能指针:auto_ptr.unique_ptr.sh ...

  9. Effective Modern C++:04智能指针

    裸指针有着诸多缺点:裸指针的声明中看不出它指向的是单个对象还是数组:裸指针的声明中也无法看出使用完它指向的对象后是否需要删除,也就是声明中看不出裸指针是否拥有其指向的对象:即使知道要析构裸指针指向的对 ...

随机推荐

  1. Springboot上传文件临时目录无效

    一个奇葩问题,虽然解决了,但还是没弄清楚,小记一笔. 年后回来,测试人员对年前的3次迭代的功能进行了回归测试,然后发现所有excel导入的功能都失效了.作为后台开发人员,当然是第一时间打开运行日志排查 ...

  2. 2019-6-23-win10-uwp-应用放到桌面

    title author date CreateTime categories win10 uwp 应用放到桌面 lindexi 2019-06-23 11:11:30 +0800 2019-06-2 ...

  3. 深度解读 - Windows 7核心图形架构细致分析(来自微软)

    编辑:马志文 时间: 2009-12-29 如现在大家所想的那样, Windows7 其实是 Windows Vista 的改进版. Windows 7 在 Windows Vista 的基础上进行了 ...

  4. 路由器OpenWrt如何脱机(离线)下载BT文件

    路由器OpenWrt如何脱机(离线)下载BT文件 1.首先到如下网址下载OpenWrt固件(确保为路由器正确型号). http://downloads.openwrt.org/snapshots/tr ...

  5. N9K 40G接口一分4*10G配置

    Breakout InterfacesCisco NX-OS supports the breakout of high bandwidth 40G interfaces at the module ...

  6. 【Kubernetes】架构全图

    K8s是什么 Kubernetes是Google开源的容器集群管理系统.它构建在Docker技术之上,为容器化的应用提供资源调度.部署运行.服务发现.扩容缩容等一整套功能. K8s能做什么 ①容器的自 ...

  7. SQL SQL Index SEEK VS Lookup

    SEEK - find everything from index Lookup - find key from index, then visit table to find other row d ...

  8. vagrant在windows下的安装和配置(一)

    记录一下安装和配置过程中的一些坑步骤一分别下载vagrant和VirtualBox,我这里下载的是vagrant_1.9.1.msi 和 VirtualBox-5.1.14-112924-Win.ex ...

  9. 聊聊最近撸Spring源码感悟

    一.前言     最近一段时间撸了Spring IOC.AOP.Transactional源码,这篇文章聊聊我写了哪些小玩意,这可能就是阅读源码以后最大收获.希望大家在里面能学习一些什么东西吧: 二. ...

  10. 大学最新毕业论文参考文献,包含java,jsp,mysql,Android,sql,PHP

    每当毕业论文写到最后需要参考文献时,往往是很令人头疼的,因为有的老师对参考文献的要求是很多的,比如需要国内的和国外的,时间必须是近三年的,满足XXX要求的文献至少需要三篇以上等等.今天我就来给大家整理 ...