C++ new与delete的使用规范

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

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏流川疯编写程序的艺术

leetcode 217 Contains Duplicate 数组中是否有重复的数字

Contains Duplicate Total Accepted: 26477 Total Submissions:...

13610
来自专栏流川疯编写程序的艺术

使用vs2010 opencv2.4.4编译release版本程序

2.在链接-》输入-》附加依赖库,中添加,相应的staticlib库目录中的所有条目

12310
来自专栏流川疯编写程序的艺术

leetcode 5 Longest Palindromic Substring--最长回文字符串

Given a string S, find the longest palindromic substring in S. You may assume th...

30730
来自专栏指点的专栏

2017 年终总结 --- 忙碌而又颇有意义的一年

其实一早就有写年终总结的冲动,真到了写总结的时候又不知道从何开始。按理来说年终总结一般都是在年度结束的时候。好吧,我得承认,我的想法有点特殊。因为是在校学生,所...

12320
来自专栏流川疯编写程序的艺术

OpenCV问题集锦,图片显示不出来,WaitKey(0),imread()不能读图片,未经处理的异常,等问题集合

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.n...

23530
来自专栏流川疯编写程序的艺术

vs 2010调用matlab dll显示窗口核心代码

代码下面有,添加滚动条的窗口,这个地方主要是一个事件,点击按钮后,调用matlab代码生成的dll进行运算,完后显示的结果窗口显示出来,完后移动窗口到指定位置

12920
来自专栏流川疯编写程序的艺术

windows下,c /c++实现磁盘扫描,结合配置文件,读取特定后缀文件目录代码

http://download.csdn.net/detail/wangyaninglm/8301303

38730
来自专栏老欧说安卓

Android开发笔记(六十九)JNI实战

NDK全称为Native Development Kit,意即原生的开发工具,NDK允许开发者在APP中通过C/C++代码执行部分程序。它是Android提...

18540
来自专栏流川疯编写程序的艺术

leetcode 37. Sudoku Solver 36. Valid Sudoku 数独问题

三星机试也考了类似的题目,只不过是要针对给出的数独修改其中三个错误数字,总过10个测试用例只过了3个与世界500强无缘了

19710
来自专栏对角巷

Caffe(11)--YOLOv1的Detection层实现

1、yolov1论文中分为7*7=49个网格 2、对于3类的目标检测,每个网格有classes+num*(coords+confidence)=3+2*(4+...

18430

扫码关注云+社区

领取腾讯云代金券

年度创作总结 领取年终奖励