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++的程序危害极大,也是很多人对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释放空间是一种“标准的”、安全的做法。 例如下面的程序。

#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++语言中,一个构造函数不能调用本类的另一个构造函数,其原因就是为了防止构造函数的相互调用打破了内存申请与释放之间的这种对应关系。

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

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏liulun

Nim教程【十一】

引用类型和指针类型 不同的引用可以只想和修改相同的内存单元 在nim中有两种引用方式,一种是追踪引用,另一种是非追踪引用 非追踪引用也就是指针,指向手动在内存中...

2116
来自专栏漏斗社区

学会代码执行函数,让老哥带你勇闯天涯!

最近研究PHP的一些危险函数,先写下代码执行函数的归纳,主要是参考自官方手册的解读,并附上了一些dogBypass的一句话,为什么是dog呢?因为在我看来dog...

3726
来自专栏青玉伏案

窥探Swift编程之错误处理与异常抛出

在Swift 2.0版本中,Swift语言对其错误处理进行了新的设计,当然了,重新设计后的结果使得该错误处理系统用起来更爽。今天博客的主题就是系统的搞一下Swi...

1955
来自专栏Python

浅淡python中with的用法,上下文管理器

例子一 首先来看一段代码: class Foo(object): def __init__(self): print('实例化一个对象...

21810
来自专栏散尽浮华

Nginx的location配置规则梳理

Nginx几乎是当下绝大多数公司在用的web应用服务,熟悉Nginx的配置,对于我们日常的运维工作是至关重要的,下面就Nginx的location配置进行梳理:...

2287
来自专栏JavaEE

java基础知识01

正所谓万丈高楼平地起,有了扎实的基础才能进阶更深奥的课程,才能让你后面的走得更轻松,学Java亦是如此!所以千万不能忽略基础的重要性,下面一起来温习一下那些容易...

1042
来自专栏微信公众号:Java团长

详解Java类的生命周期

最近有位细心的朋友在阅读笔者的文章时,对java类的生命周期问题有一些疑惑,笔者打开百度搜了一下相关的问题,看到网上的资料很少有把这个问题讲明白的,主要是因为目...

972
来自专栏xingoo, 一个梦想做发明家的程序员

数字按照不同格式转换成字符串

  如果自己写函数,不使用itoa怎么判断呢?   我们用通常的办法,对数字进行每位的除商,得到后与字符'0'相加。 flag = 0; ...

20710
来自专栏LIN_ZONE

PHP 反射的简单使用

1554
来自专栏轮子工厂

Java多线程学习

提醒一下大家:main方法其实也是一个线程。在java中所以的线程都是同时启动的,至于什么时候,哪个先执行,完全看谁先得到CPU的资源。

1282

扫码关注云+社区

领取腾讯云代金券