【C++学习笔记】C++ 使用new与delete时,你注意到这3个地方了吗?

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

 

1.new与delete需一一对应

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

#include <iostream>

usingnamespacestd;

intmain()

{

int*p;

p=newint(3);

if(p)

{

deletep;

}

deletep;

return0;

}

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

#include <iostream>

usingnamespacestd;

intmain()

{

int*p,*q,*one;

one=newint;

if(one)

{

cout<<one<<endl;

}

deleteone;

p=newint(3);

if(p)

{

cout<<p<<endl;

}

deleteone;//假设这句语句是程序员不小心加上的

q=newint(5);

if(q)

{

cout<<q<<endl;

}

cout<<(*p)+(*q)<<endl;

deletep;

deleteq;

}

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

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[]运算符的区别。

classTest

{

public:

Test() { cout<<"ctor"<<endl; }

~Test() { cout << "dtor"<< endl; }

};

//segment1

Test* pArray1 = newTest[3];

deletepArray1;

//segment2

Test* pArray2 = newTest[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=newint[3];

//processing code

deletepDArr;   //等同于delete[] pDArr

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

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

typedefintHeight[NUM];

int* pHeight=newHeight;

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

deletepHeight;        //wrong,但容易错误地使用delete

delete[] pHeight;   //right

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

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

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

#include <iostream>

usingnamespacestd;

classStudent

{

char* name;

public:

Student()

{

cout<<"Default constructor"<<endl;

}

Student(char*);

~Student();

};

Student::Student(char*s)

{

//Student();//此句运行时报错,构造函数不能调用其他构造函数

cout<<"In constructor,allocating space"<<endl;

name=newchar[strlen(s)+1];

strcpy(name,s);

cout<<"name:"<<name<<endl;

}

Student::~Student()

{

cout<<"In destructor, free space"<<endl;

deletename;

}

intmain()

{

Student s1("张三");

}

程序运行输出:

In constructor,allocating space

name:张三

In destructor, free space

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

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

以上就是C++ 使用new与delete需注意的原则的详细内容,有什么问题欢迎大家评论区留言。

(0)

相关推荐

  • C++内存管理之shared

     ----------------------------------------shared_ptr--------------------------------------- 引子 c++中动态 ...

  • c++动态内存分配

    下面随笔是关于c++动态内存分配. 动态申请内存操作符 new new 类型名T(初始化参数列表) 功能:在程序执行期间,申请用于存放T类型对象的内存空间,并依初值列表赋以初值. 结果值:成功:T类型 ...

  •  C++11中shared

    在C++中,动态内存的管理是通过一对运算符来完成的:new,在动态内存中为对象分配空间并返回一个指向该对象的指针,可以选择对对象进行初始化:delete,接受一个动态对象的指针,销毁该对象,并释放与之 ...

  • C++析构函数详解

    首页 >编程笔记 >C++笔记阅读:42,689C++析构函数详解C语言中文网推出辅导班啦,包括「C语言辅导班.C++辅导班.算法/数据结构辅导班」,全部都是一对一教学:一对一辅导 + 一 ...

  • 《子平真诠》学习笔记(三十)--论时说以讹传讹

    八字本有定理,理之不明,遂生导端,妄言妄听,牢不可破.如论干支,则不知阴阳之理,而以俗书体象歌诀为确论:论格局,则不知专寻月令,而以拘泥外格为活变:论生克,则不察喜忌,而以伤旺扶弱为定法:论行运,则不 ...

  • 一则公报案例学习笔记:对修改股东出资期限应否适用资本多数决规则的思考|审判研究

    一.问题的提出 2021年第3期<最高人民法院公报案例>刊登了鸿大(上海)投资管理有限公司与姚锦城公司决议纠纷上诉案,裁判要旨为:"公司股东滥用控股地位,以多数决方式通过修改出资 ...

  • JAVA多线程学习笔记整理

    多线程: 三种创建方法 继承Thread类,以线程运行内容重写run方法,创建Thread对象并用start方法启动该线程. (匿名内部类) (Lambda表达式) 实现Runable接口,以线程运行 ...

  • 周哥学习笔记(2021.5.8)

    心理界限存在的意义,正是为了帮助人们控制情绪进入的量,不至于太过冷漠或太过投入,让我们保持一个合适的距离与外界互动. 人没有办法只通过吸收变得更美好和丰富,它必须通过大胆的碰撞和创造.如果不能保持足够 ...

  • 【学习笔记】控制角色移动的N种方法,但都离不开重复执行

    [学习笔记]控制角色移动的N种方法,但都离不开重复执行 今天我们讲一下控制角色移动的多种方法,因为缺少操作实例,希望课下同学们结合例子好好练习. 首先,我们说一下控制角色移动的多种方法.最比较常见的就 ...

  • 胡希恕伤寒论学习笔记——42

    42.太阳病,外证未解,脉浮弱者,当以汗解,宜桂枝汤. 字面意思是说:太阳病,外证依然存在,脉是浮弱的,治疗上依然需要通过出汗的方法,这时应该用桂枝汤一类的方剂. "宜"字说明不是 ...

  • 量柱擒涨停 - 量柱战法学习笔记(2)

    四.倍量战术 1.倍量的理解 [形态特征]:与前一个交易日比较高出1倍或1倍以上,就是倍量(4倍以上为发烧柱) ; [本质特征]:体现主力强势态度,主动(倍量阳/阴)买/卖盘吸筹坚决; [位置性质]: ...

  • 胡希恕伤寒论学习笔记——43

    43.太阳病,下之微喘者,表未解故也,桂枝加厚朴杏子汤主之. 桂枝加厚朴杏子汤方 桂枝三两 芍药三两 厚朴二两(炙,去皮) 杏仁五十枚(去皮尖)甘草二两(炙) 生姜三两(切)大枣十二枚(掰) 上七味, ...

  • 学习笔记:信息技术

    学习笔记:信息技术