1. 一个错误释放内存的例子

下面的场景会有什么错?

 std::string *stringArray = new std::string[];

 ...

 delete stringArray

一切看上去都是有序的。new匹配了一个delete。但有一些地方确实是错了。程序的行为是未定义的。至少来说,stringArray指向的100个string对象中的99个看上去都不能被正确释放,因为他们的析构函数可能永远不会被调用。

2. 使用new 和delete时究竟做了啥?

当你使用一个new表达式(通过使用new动态的创建一个对象)时,会发生两件事情。第一,内存被分配(通过一个叫做operator new的函数,看Item 49和Item 51)。第二,在分配的内存上调用了一个或多个构造函数。当你使用一个delete表达式时,另外两件事情会发生:在内存上调用了一个或者多个析构函数,然后内存被解除分配(通过调用叫做operator delete的函数,见 Item 51)。关于delete的一个重要的问题是:在即将被删除的内存中究竟有多少对象?这个问题的答案决定了有多少个析构函数必须被调用。

3. new和delete不配对使用为啥会出错?

实际上,下面这个问题更加简单:被删除的指针是指向一个单独的对象还是指向数组的所有对象?这是个关键的问题,因为单个对象的内存分配通常情况下同数组的内存分配是不一样的。特别的,一个数组的内存通常包含了数组的大小,因此delete很容易就会知道需要调用多少个析构函数。单个对象的内存却没有这样的信息。你可以将内存不同分配想象成下面这个样子,n是数组的大小:

当然这只是一个例子。编译器不需要这么实现,虽然很多编译器确实是这么实现的。

当你在一个指针上使用delete时,delete能够知道数组容量信息是否存在的唯一方法就是通过你来告诉它。如果当你使用delete时用了“[]”,delete认为指针指向一个数组。否则,它会认为它在指向一个单一的对象:

 std::string *stringPtr1 = new std::string;

 std::string *stringPtr2 = new std::string[];

 ...

 delete stringPtr1; // delete an object

 delete [] stringPtr2; // delete an array of objects

4. new和delete不配对使用会有什么后果?

如果你在stringPtr1上使用“[]”将会发生什么?结果是未定义的,但是结果不会太好。假设内存分布如上图所示,delete会读取一些内存并把它所读到的解释为一个数组容量,接下来就开始多次调用析构函数,却忽略的以下事实:它处理的内存不但不是一个数组,也可能并没有包含它正忙着释放的那种类型的对象。

如果你不在stringPtr2上使用“[]”会发生什么?结果也是未定义的,但是你可以看到这会导致过少的构造函数被调用。此外,对于像int的内建类型来说结果也是未定义的(有时甚至是有害的),虽然内建类型没有析构函数。

规则很简单:如果你在一个new表达式中使用”[]”,你必须在对应的delete表达式中使用”[]”,反之亦然

当你实现一个包含指向动态分配内存的指针的类,并且同时提供多个构造函数的时候,你需要将上面的重要规则记在心中,因为你必须当心在对构造函数中对指针成员进行初始化时,new必须使用相同的形式。如果你不这么做,你又怎么能知道在析构函数中将使用什么形式的delete呢?

5. 使用typedef时需要注意new和delete的配对使用

对于倾向于使用typedef的人来说这条规则同样值得注意,因为这意味着typedef的作者必须指出使用new来创建typedef类型的对象时,使用什么形式的delete对其进行销毁。看下面的例子:

 typedef std::string AddressLines[]; // a person’s address has 4 lines,

 // each of which is a string

因为AddressLines是一个数组,new应该这么使用:

 std::string *pal = new AddressLines; // note that “new AddressLines”

 // returns a string*, just like

 // “new string[4]” would

使用delete的形式必须和new相匹配:

 delete pal; // undefined!

 delete [] pal; // fine

为了避免这种混淆,不如放弃在数组类型上使用typedef。这很容易,因为标准c++库(见Item 54)中包含string,vector和模板,使得对动态分配数组的需求几乎将为0。这里我们举个例子,AddressLines可以被定义成由strings组成的vector,也就是类型 vector<string>。

读书笔记 effective c++ Item 16 成对使用new和delete时要用相同的形式的更多相关文章

  1. Effective C++(16) 成对使用new和delete时要采取相同的形式

      问题聚焦:     我们都知道,new和delete要成对使用,但是有时候,事情往往不是按我们预期的那样发展.     对于单一对象和对象数组,我们要分开考虑     遇到typedef时,也需要 ...

  2. 读书笔记 effective c++ Item 50 了解何时替换new和delete 是有意义的

    1. 自定义new和delete的三个常见原因 我们先回顾一下基本原理.为什么人们一开始就想去替换编译器提供的operator new和operator delete版本?有三个最常见的原因: 为了检 ...

  3. 读书笔记 effective c++ Item 31 把文件之间的编译依赖降到最低

    1. 牵一发而动全身 现在开始进入你的C++程序,你对你的类实现做了一个很小的改动.注意,不是接口,只是实现:一个私有的stuff.然后你需要rebuild你的程序,计算着这个build应该几秒钟就足 ...

  4. 读书笔记 effective c++ Item 18 使接口容易被正确使用,不容易被误用

    1. 什么样的接口才是好的接口 C++中充斥着接口:函数接口,类接口,模板接口.每个接口都是客户同你的代码进行交互的一种方法.假设你正在面对的是一些“讲道理”的人员,这些客户尝试把工作做好,他们希望能 ...

  5. 读书笔记 effective c++ Item 30 理解内联的里里外外 (大师入场啦)

    最近北京房价蹭蹭猛涨,买了房子的人心花怒放,没买的人心惊肉跳,咬牙切齿,楼主作为北漂无房一族,着实又亚历山大了一把,这些天晚上睡觉总是很难入睡,即使入睡,也是浮梦连篇,即使亚历山大,对C++的热情和追 ...

  6. 读书笔记 effective c++ Item 51 实现new和delete的时候要遵守约定

    Item 50中解释了在什么情况下你可能想实现自己版本的operator new和operator delete,但是没有解释当你实现的时候需要遵守的约定.遵守这些规则并不是很困难,但是它们其中有一些 ...

  7. 读书笔记 effective c++ Item 54 让你自己熟悉包括TR1在内的标准库

    1. C++0x的历史渊源 C++标准——也就是定义语言的文档和程序库——在1998被批准.在2003年,一个小的“修复bug”版本被发布.然而标准委员会仍然在继续他们的工作,一个“2.0版本”的C+ ...

  8. 读书笔记 effective c++ Item 52 如果你实现了placement new,你也要实现placement delete

    1. 调用普通版本的operator new抛出异常会发生什么? Placement new和placement delete不是C++动物园中最常遇到的猛兽,所以你不用担心你对它们不熟悉.当你像下面 ...

  9. 条款16:成对使用new和delete时,采取相同的形式

    问题聚焦:     我们都知道,new和delete要成对使用,但是有时候,事情往往不是按我们预期的那样发展.     对于单一对象和对象数组,我们要分开考虑.     遇到typedef时,也需要搞 ...

随机推荐

  1. 创建和使用MySQL计划事件

    查看事件调度程序线程的状态: SHOW PROCESSLIST; 要启用和启动事件调度程序线程命令: SET GLOBAL event_scheduler = ON; 禁用和停止事件调度程序线程: S ...

  2. UITextField 之 手势收起键盘

    1. 注册手势 /** * 注册手势 */ -(void)gestureReg{ //放弃第一响应者 UITapGestureRecognizer * tap = [[UITapGestureReco ...

  3. iOS 之 通知

    步骤一,注册消息: [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(getUserProfileSu ...

  4. eQTL

    首先QTL是数量性状位点,比如身高是一个数量性状,其对应的控制基因的位点就是一个数量性状位点,而eQTL就是控制数量性状表达位点,即能控制数量性状基因(如身高基因)表达水平高低的那些基因的位点. 数量 ...

  5. Angular - - ngInclude、ngTransclude

    这两个都是HTML DOM嵌入指令 ngInclude 读取,编译和插入外部的HTML片段. 格式:ng-include=“value”<ng-include src=”value” onloa ...

  6. JS 工具 构建工具

    1.gruntjs http://www.gruntjs.net/ 2.bootstrap http://www.bootcss.com/ 3.

  7. 后台邮箱配置SMTP函数,如何把发件人设置为自定义昵称

    默认的php的mail函数,太坑爹! 用PHPCMS配置好SMTP后,虽然能发送邮件. 但是收件人一项,显示的是邮箱地址@前面的内容. 今天研究了一下午,终于在一个论坛里找到了答案. 原来PHPCMS ...

  8. EhLib DBGridEh组件在Delphi中应用全攻略总结(转)

    EhLib DBGridEh组件在Delphi中应用全攻略总结(转) http://blog.sina.com.cn/s/blog_94b1b40001013xn0.html 优化SQL查询:如何写出 ...

  9. CentOS6+MySQL5.6二进制安装

    一般我们安装mysql采用二进制安装的方式就足以满足我们的生产环境了,不过需要我们配置my.cnf文件 从官网下载二进制MySQL,选择Linux-Generic,最后这两个是二进制包 http:// ...

  10. C/C++将一个整型数组拼接成一个字符串

    参考: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 #include <stdio.h> #include <string.h> int ma ...