C++的动态内存管理是通过new和delete两个操作来完成的,即用new来申请空间,用delete来释放空间。在使用new和delete时,注意以下原则。

1.new与delete需一一对应

用new操作申请空间,如果申请成功,必须在以后的某个时刻用delete释放该空间,既不能忘记释放,也不能多次释放。前者会引起内存泄露,后者会引起运行时错误。如下面的程序。

#include <iostream>
using namespace std;int main()
{int *p;p=new int(3);if(p){delete p;}delete p;return 0;
}

以上程序对指针p所指向的空间进行两次释放,这种内存错误对C++程序危害极大,也是很多人对C++忘而却步的原因。多次释放同一块内存空间,并不一定立即引起程序运行错误,也不一定会导致程序运行的崩溃,这跟具体的编译器实现有关。但是,多次释放同一块内存空间绝对是一个编程错误,这个编程错误可能会在其后的某个时刻导致其他的逻辑错误的发生,从而给程序的调试和纠错带来困难。考察如下程序。

#include <iostream>
using namespace std;int main()
{int *p,*q,*one;one=new int;if(one){cout<<one<<endl;}delete one;p=new int(3);if(p){cout<<p<<endl;}delete one;//假设这句语句是程序员不小心加上的q=new int(5);if(q){cout<<q<<endl;}cout<<(*p)+(*q)<<endl;delete p;delete q;
}

程序通过编译,运行结果如下:

003289A0
003289A0
003289A0
10

程序运行过程中会产生中断。从程序的输出可以看出,在将指针one所指向的空间释放后,为指针p申请的空间就是原来one所指向的空间。由于不小心在为p分配空间之后再次使用了delete one,导致q申请到的空间就是原来p所申请的空间,这样赋给*q的值就改写了原来p所指向的单元的值,导致最后输出结果为10。由此可知,多次释放同一块内存空间,即使不导致程序运行中断,也会破坏环境,使指针与所对应的空间的隶属关系出现混乱,从而导致逻辑错误。在大型程序设计中,这种逻辑错误的查找会变得十分费时费力。

**注意:**当指针p的值为NULL时,多次使用delete p并不会带来麻烦,因为释放空指针的空间实际上不会导致任何操作。所以,将“不用”的指针设置为NULL是一个好的编程习惯。

2.new[]与delete[]需一一对应

在申请对象数组时,需要使用new[]运算符,与之对应,释放对象数组时,需要使用delete[]运算符。这一点与C语言有所区别,C中无论申请单个还是多个对象,均使用malloc()/free()函数。首先看一下delete与delete[]运算符的区别。

class Test
{
public:Test() { cout<<"ctor"<<endl; }~Test() { cout << "dtor" << endl; }
};//segment1
Test* pArray1 = new Test[3];
delete pArray1;//segment2
Test* pArray2 = new Test[3];
delete[] pArray2;

其中代码片段segment1运行结果如下:

ctor
ctor
ctor
dtor

segment2运行结果如下:

ctor
ctor
ctor
dtor
dtor
dtor

可以看出,delete与delete[]区别在于释放对象数组时,delete只调用了一次析构函数,delete[]调用了三次析构函数,完成了对象数组的释放。实际上,在使用new和new[]申请内存空间时,会申请一段额外的内存来保存用户申请的内存空间大小,元素个数等信息。当使用delete[]释放内存空间时,会逐个调用对象的析构函数并完成最终的内存空间的释放。使用delete释放对象数组时,则只会调用单个对象的析构函数,造成内存泄漏。符号[]告诉编译器,在delete一块内存时,先去获取内存保存的元素个数,然后一一清理。所以使用delete释放new[]申请的内存空间和使用delete[]释放new申请的内存空间都错误的做法。

具体使用时,需要注意以下两点:
(1)对于内置数据类型,因为没有构造和析构函数,所以使用delete和delete[]的效果是一样的。比如:

int* pDArr=new int[3];
//processing code
delete pDArr;   //等同于delete[] pDArr

对于内置数据类型,虽然可以使用delete完成对象数组内存空间的释放,但是为了保证代码的可读性,建议使用delete[]来完成。所以,new[]与delete[]使用时应一一对应。

(2)对于经常使用typedef的程序员来说,很容易new[]与delete的混用,例如有如下操作:

typedef int Height[NUM];
int* pHeight=new Height;

这个情况应该使用delete还是delete[]呢?答案如下:

delete   pHeight;        //wrong,但容易错误地使用delete
delete[] pHeight;   //right

为了避免出现上面的错误,建议不要对数组使用typedef,或者采用STL中的vector代替数组。

3.构造函数中的new/new[]与析构函数的中delete/delete[]需一一对应

当类的成员中有指针变量时,在构造函数中用new申请空间并且在析构函数中用delete释放空间是一种“标准的”、安全的做法。例如下面的程序。

#include <iostream>
using namespace std;class Student
{char* name;
public:Student(){cout<<"Default constructor"<<endl;}Student(char*);~Student();
};Student::Student(char*s)
{//Student();//此句运行时报错,构造函数不能调用其他构造函数cout<<"In constructor,allocating space"<<endl;name=new char[strlen(s)+1];strcpy(name,s);cout<<"name:"<<name<<endl;
}Student::~Student()
{cout<<"In destructor, free space"<<endl;delete name;
}int main()
{Student s1("张三");
}

程序运行输出:

In constructor,allocating space
name:张三
In destructor, free space

由于任何一个对象,其构造函数只调用一次,其析构函数也只调用一次,这样就能保证运行时new和delete操作是一一对应的,也就保证了内存管理的安全性。

在C++中,一个构造函数不能调用本类的另一个构造函数,其原因就是为了防止构造函数的相互调用打破了内存申请与释放之间的这种对应关系。


参考文献

[1]C++高级进阶教程.陈刚.P260-P264
[2]编写高质量代码改善C++程序的150个建议.李健.P69-P71

C++ new与delete的使用规范相关推荐

  1. JavaScript知识点之:delete操作符

    delete操作符来删除一个隐式声明的全局变量,也就是没有使用var定义的全局变量. 如果delete操作符删除成功, 则被delete的属性已从所属的对象上彻底消失, 随后,该对象的原型上可能存在的 ...

  2. 【项目总结】项目开发规范

    目录 背景 后端规范 一.接口api规范 1.接口风格接口风格统一采用restful规范 2.接口api命名规范 3.接口参数规范 4.接口响应数据/状态码规范 4.1响应数据结构 4.2Http状态 ...

  3. MySQL工具名字_MySQL客户端工具及SQL讲解

    1.用于数据库的连接管理 #MySQL接口自带的命令 \h 或 help 或? 查看帮助 \G 格式化查看数据(key:value) \T 或 tee 记录日志 \c(5.7可以ctrl+c) 结束命 ...

  4. 学习笔记:c++ newdelete关键字及其自定义

    文章目录 一.new运算符说明 1.作用 2.标准库函数 3.运行原理: 4.常见用法: 5.size_t如何传递 二.delete函数 1.作用 2.标准库函数 3.运行原理: 4.常见用法: 三. ...

  5. 【连载】JavaScript高级程序设计(红宝书)阅读笔记

    < script >的几个属性 async:可选.表示应该立即开始下载脚本,但不能阻止其他页面动作,比如下载资源或等待其他脚本加载.只对外部脚本文件有效.  crossorigin:可 ...

  6. Drf简介,什么是drf

    Drf简介 在了解Drf之前先了解几个知识点 web开发模式 混合开发 # 前后端混合开发:- 模板的渲染是在后端完成:比如:后端: name = 'gary'前端: var name = {{ na ...

  7. SpringBoot项目--智慧农业

    学点技术不容易,感谢实验室同门 控制层(controller)的职能是负责读取视图表现层的数据,控制用户的输入,并调用业务层的方法: 业务层(service)需要根据系统的实际业务需求进行逻辑代码的编 ...

  8. C/C++ 编程规范(02)— 标识符命名

    标识符的命名要清晰.明了,有明确含义,同时使用完整的单词或大家基本可以理解的缩写,避免使人产生误解. 说明:较短的单词可通过去掉"元音"形成缩写:较长的单词可取单词的头几个字母形成 ...

  9. 2. 编程规范和编程安全指南--C/C++

    目录 1 通用安全指南 I. C/C++使用错误 1.1 不得直接使用无长度限制的字符拷贝函数 1.2 创建进程类的函数的安全规范 1.3 尽量减少使用 _alloca 和可变长度数组 1.4 pri ...

最新文章

  1. 微软全都要!Win10引入真Linux内核
  2. jQuery常用选择器总结(超详细)
  3. DHCP中继原理与配置
  4. 重置Winsock失败,在NSHHTTP.DLL中初始化函数InitHelperDll启动失败,错误代码为10107的解决方法
  5. 『晨读』纳什均衡又称为非合作博弈均衡,在一个博弈过程中,
  6. 使用python编写聊天小程序
  7. 【重要通知】关于免费升级至神策分析 2.3 版本的说明
  8. 菲涅尔单缝衍射matlab,单缝菲涅尔衍射的光强分布.pdf
  9. 操作系统4.1.4 文件的物理结构(文件分配方式)
  10. TensorFlow的常量、变量、常用函数(一)
  11. python识别中文验证码_Python实现验证码识别
  12. 超级NB的防DDOS(小量级)攻击的脚本
  13. 微信小程序流量主开通 (如何赚钱)
  14. Python函数式编程 及案例
  15. html+css模仿微信主页面
  16. Python网络爬虫及数据可视化(软科中国大学专业排名|计算机科学与技术)
  17. c语言中“|”和“||”区别
  18. 漫画《修车危情》漫画修车危情第一话完整版
  19. Axure PR9学习随笔
  20. Oracle 分页查询 rownum 和 offset

热门文章

  1. 0c-36-自动释放池应用场景
  2. 【解决方法】jdb2/sdb1-8 io使用过高
  3. file does not exist 阿里云OSS图片上传遇到的问题
  4. php imagick
  5. linux echo设置颜色
  6. IP记录Linux所有用户操作日志的方法(附脚本)
  7. 考试全程指导读书笔记1 -Chap1 信息系统基础
  8. 官方数据:程序员年纪越大越出色、越稀有
  9. 【博客话题】什么样的学生你都能教好——亲爱的魏老师
  10. C#关键字operator