1.简介

C++ 作为一门应用广泛的高级编程语言,却没有像 Java、C# 等语言拥有垃圾回收(Garbage Collection )机制来自动进行内存管理,这也是C++ 一直被诟病的一点。C++ 在发展的过程中,一直致力于解决内存泄漏,C++ 虽然基于效率的考虑,没有采用垃圾回收机制,但从 C++98 开始,推出了智能指针(Smart Pointer)来管理内存资源,以弥补 C++ 在内存管理上的技术空白。

智能指针是 C++ 程序员们一件管理内存的利器,使用智能指针管理内存资源,实际上就是将申请的内存资源交由智能指针来管理,是 RAII 技术的一种实现。RAII 是 C++ 的之父 Bjarne Stroustrup 教授提出的概念,RAII 全称是“Resource Acquisition is Initialization”,直译过来是“资源获取即初始化”,也就是说在构造函数中获取资源,在析构函数中释放资源。因为 C++ 的语言机制保证了,当一个对象创建的时候,自动调用构造函数,当对象超出作用域的时候会自动调用析构函数。所以,在 RAII 的指导下,我们应该使用类来管理资源,将资源和对象的生命周期绑定。

“资源获取即初始化”,在使用智能指针管理内存资源时,“资源”指的是通过 new 或 malloc 申请的内存资源,“初始化”指的是使用申请的内存资源来初始化栈上的智能指针类对象。使用智能指针管理内存资源的好处显而易见,通过智能指针对象在生命周期结束时,自动调用析构函数,在析构函数中完成对内存资源的释放,即自动调用内存资源的释放代码,避免因忘记对内存资源的释放导致内存泄漏。

2.实例

下面看一个使用由 C++11 引入的智能指针 unique_ptr 来管理内存资源的例子。

#include <memory>
#include <iostream>
using namespace std;class A
{
public:A() {}~A() {cout<<"A's destructor"<<endl;}void Hello() {cout<<"use smart pointer to manage memory resources as far as possible"<<endl;}
};int main()
{unique_ptr<A> pA(new A);pA->Hello();return 0;
}

程序输出:

use smart pointer to manage memory resources as far as possible
A's destructor

可见在 main() 函数结束后,类 A 的析构函数被自动调用,完成了内存资源的释放。

在创建智能指针对象时,也可以暂时不指定内存资源,先创建一个空的智能指针对象。空智能指针对象不可以进行任何操作,但可以使用 get() 成员函数来判断是否存在内存资源,如果为空则可以指定内存资源。类似于如下操作:

unique_ptr<int> pInt;
if (pInt.get()==nullptr)
{pInt.reset(new int(8));cout<<*pInt<<endl;
}

使用 unique_ptr 智能指针来管理内存资源时,是对内存资源的独占式管理,即内存资源的所有权不能进行共享,同一时刻只能有一个 unique_ptr 对象占有某个内存资源。如果发生赋值或拷贝构造,则会在编译期报错,因为unique_ptr禁止了拷贝语义,提高了代码的安全性。

unique_ptr<int> pInt(new int(8));
unique_ptr<int> pInt1=pInt;   //编译报错
unique_ptr<int> pInt2(pInt);   //编译报错

当然,可以通过移动语义完成内存资源的所有权转移,转移之后,原智能指针对象变为空智能指针对象,不能再对内存资源进行任何操作,否则会发生运行时错误,但我们也可以使用get()成员函数进行判空处理。

unique_ptr<int> pInt(new int(8));
unique_ptr<int> pInt1=std::move(pInt);        //转移所有权
*pInt=6;                                                               //对空智能指针进行赋值操作将报运行时错误
if(!pInt.get())                                                     //判空处理更安全
{*pInt=6;
}

独占式的内存资源管理可以使用 unique_ptr 来完成,但是如果想对内存资源进行共享式管理,那么 unique_ptr 就无能为力了。shared_prt 使用引用计数来实现对内存资源的共享式管理,当对内存资源的引用计数变为0时,由最后一个对内存资源拥有管理权的智能指针对象完成对内存资源的释放。

#include <memory>
#include <iostream>
using namespace std;class A
{
public:A() {}~A(){cout << "A's destructor" << endl;}void Hello(){cout << "use smart pointer to manage memory resources as far as possible" << endl;}
};int main()
{shared_ptr<A> spInt(new A);      //接管内存资源cout << "reference count "<<spInt.use_count() << endl;shared_ptr<A> spInt1 = spInt;  //spInt1获取内存资源的管理权spInt1->Hello();cout << "reference count " << spInt.use_count() << endl;spInt1.reset();                        //spInt1放弃对内存资源的管理权cout << "reference count " << spInt.use_count() << endl;
}

程序编译运行结果:

reference count 1
use smart pointer to manage memory resources as far as possible
reference count 2
reference count 1
A's destructor

3.智能指针使用注意事项

智能指针虽然增强了安全性,避免了潜在的内存泄漏,但是我们在使用时还是应该遵守一定的规则,以保证代码的健壮性。
(1)smart_ptr<T> 不等于 T*,使用时不能完全按照T*来使用。因为smart_ptr<T>本质上是类对象,一个用于管理内存资源的智能指针类对象,而T*是一个指向类型T的指针,二者不能随意地转换和赋值;
(2)使用独立的语句将newed对象置入智能指针,因为使用临时智能指针对象可能会引发内存泄漏。比如下面的语句:

process(shared_ptr<A>(new A),foo());

实际上对 process() 函数调用时编译器需要完成如下三步为process()准备好实参。
(2.1)调用函数foo();
(2.2)执行new A表达式;
(2.3)调用shared_ptr<A>构造函数,初始化智能指针对象。
实际上,不同的编译器在执行上述三个语句时可能会有不同的顺序,如果编译器将(2.2)放在(2.1)之前执行,执行顺序如下:
(2.1)执行new A表达式;
(2.2)调用函数foo();
(2.3)调用shared_ptr<A>构造函数,初始化智能指针对象。
如果在调用函数foo()时抛出异常,那么new A表达式产生的指向堆对象指针将会丢失,于是产生了内存泄漏。解决办法就是使用独立的语句将newed对象置入智能指针,做法如下:

shared_ptr<A> spA(new A);
process(spA,foo());

参考文献

[1]改善C++程序的150个建议[M].建议34:用智能指针管理通过new创建的对象
[2]Effective C++ 中文第三版[M].条款13:以对象管理资源
[3]Effective C++ 中文第三版[M].条款14:以独立语句将newed语句置入智能指针

C++ 以智能指针管理内存资源相关推荐

  1. C++ 智能指针 :内存泄漏、 RAII、智能指针、auto_ptr、unique_ptr、shared_ptr、weak_ptr、定制删除器deleter

    文章目录 内存泄漏 什么是内存泄漏 内存泄漏的危害: 如何避免内存泄漏 RAII 智能指针 auto_ptr unique_ptr shared_ptr 循环引用问题 weak_ptr 定制删除器 内 ...

  2. C++智能指针管理类

    1.程序猿明白的进行内存释放 对于c++程序猿,最头脑的莫过于对动态分配的内存进行管理了.c++在堆上分配的内存.须要程序猿负责对分配的内存进行释放.但有时内存的释放看起来并不件非常轻松的事,例如以下 ...

  3. 智能指针和内存管理小结

    ·概述: 主要是两个库:smart_ptr库和pool库. smart_ptr库主要解决的问题是指针的内存泄漏和垃圾回收问题:pool则是解决内存分配问题. 感觉还是smart_ptr库比较好用一些, ...

  4. 【C++ 语言】智能指针 引入 ( 内存泄漏 | 智能指针简介 | 简单示例 )

    文章目录 I . 智能指针 引入 II . 智能指针 简介 III . 智能指针 简单示例 I . 智能指针 引入 1 . 示例前提 : 定义一个 Student 类 , 之后将该类对象作为智能指针指 ...

  5. ESXi主机管理内存资源的方式

    因为内存通常是最有限的资源,ESXi采用内存过量配置(Memory overcommitment,即配置后的虚拟机内存可能超过物理内存(RAM))对内存进行管理.使用内存过量配置时,ESXi必须使用技 ...

  6. 2.5w字长文爆肝 C++动态内存与智能指针一篇搞懂!太顶了!!!

    动态内存与智能指针 1.动态内存与智能指针 2.shared_ptr类 2.1.make_shared函数 2.2.shared_ptr的拷贝和赋值 2.3.shared_ptr自动销毁所管理的对象 ...

  7. 动态内存分配与智能指针

    内存分配: 静态存储区: 局部static对象 类的static数据成员 定义在任何函数之外的变量 栈区: 函数内的非static对象 动态内存分配的方式有: new和delete 智能指针(shar ...

  8. 【C++ Primer 第5版 笔记】第12章 动态内存与智能指针

    转载:http://blog.csdn.net/wwh578867817/article/details/41866315 第 12 章 动态内存 与 智能指针 静态内存 用来保存:(1)局部stat ...

  9. 标准库中的智能指针shared_ptr

    智能指针的出现是为了能够更加方便的解决动态内存的管理问题.注:曾经记得有本书上说可以通过vector来实现动态分配的内存的自动管理,但是经过试验,在gcc4.8.5下是不行的.这个是容易理解的,vec ...

最新文章

  1. 【神经网络】(18) EfficientNetV2 代码复现,网络解析,附Tensorflow完整代码
  2. sql数据库系统表和mysql系统表
  3. 【风控术语】数字金融反欺诈技术名词表
  4. YOLO-目标检测中计算AP、MAP方法
  5. 阿里云mariadb无法启动问题
  6. mysql 游标的简单_mysql 简单游标
  7. 普通路由器改4g路由器_4G宽带随心用,办公娱乐更自由,蒲公英X4C路由器体验|路由器|蒲公英|宽带|wifi|sim...
  8. bzoj 3238: [Ahoi2013]差异
  9. 01-eclipse打包运行程序总是报错java.lang.NoClassDefFoundError和ava.lang.ClassNotFoundException(打包原理)
  10. Python实现图像空域随机水印加入与提取
  11. LRGB一个带亮度值的颜色
  12. FFMPEG结构体分析:AVCodec
  13. shell逻辑运算符优先级_逻辑运算符有那些?
  14. HP刀片服务器C7000-Cisco网络模块配置指南
  15. informix 访问mysql_Informix 11.7 使用非系统用户访问数据库
  16. 关于C#中Remoting的使用
  17. C++ 取模、求余运算
  18. CPU的内部架构和工作原理(转)
  19. ChemDraw怎么激活?ChemDraw激活教程
  20. 各种说明方法的例句_11个说明方法句子

热门文章

  1. 突发:Maze 勒索团伙公开 LG 和 Xerox 的内部数据,达数十GB
  2. GitHub 推出安全新功能,帮助开源软件发现漏洞和机密信息
  3. 《简明 PHP 教程》03 第一步
  4. 14.12.1类的特殊成员1
  5. AR VR或将彻底变革广告营销行业
  6. ubuntu两个conda安装和切换
  7. 设计模式(五)——建造者模式
  8. Perlin Noise
  9. 如何理解 MySQL 中的 = 操作符?
  10. Android中SQLite应用详解(转)