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

简介: 1. 一个错误释放内存的例子 下面的场景会有什么错? 1 std::string *stringArray = new std::string[100]; 2 3 ... 4 5 delete stringArray   一切看上去都是有序的。

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

下面的场景会有什么错?

1 std::string *stringArray = new std::string[100];
2 
3 ...
4 
5 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认为指针指向一个数组。否则,它会认为它在指向一个单一的对象:

1 std::string *stringPtr1 = new std::string;
2 
3 std::string *stringPtr2 = new std::string[100];
4 
5 ...
6 
7 delete stringPtr1; // delete an object
8 
9 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对其进行销毁。看下面的例子:

1 typedef std::string AddressLines[4]; // a person’s address has 4 lines,
2 
3 // each of which is a string

 

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

1 std::string *pal = new AddressLines; // note that “new AddressLines”
2 
3 // returns a string*, just like
4 
5 // “new string[4]” would

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

1 delete pal; // undefined!
2 
3 delete [] pal; // fine

 

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


作者: HarlanC

博客地址: http://www.cnblogs.com/harlanc/
个人博客: http://www.harlancn.me/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出, 原文链接

如果觉的博主写的可以,收到您的赞会是很大的动力,如果您觉的不好,您可以投反对票,但麻烦您留言写下问题在哪里,这样才能共同进步。谢谢!

目录
相关文章
|
30天前
|
存储 Java 编译器
C++:内存管理|new和delete
C++:内存管理|new和delete
|
3月前
|
设计模式 安全 编译器
Effective C++55 笔记
Effective C++55 笔记
21 0
|
30天前
|
存储 算法 编译器
【C++ 内存管理 重载new/delete 运算符 新特性】深入探索C++14 新的/删除的省略(new/delete elision)的原理与应用
【C++ 内存管理 重载new/delete 运算符 新特性】深入探索C++14 新的/删除的省略(new/delete elision)的原理与应用
45 0
|
4月前
|
存储 编译器 C语言
【C++】内存管理(new与delete)
【C++】内存管理(new与delete)
36 0
|
4月前
|
编译器
【【C++11特性篇】【强制/禁止 】生成默认函数的关键字default&delete(代码演示)
【【C++11特性篇】【强制/禁止 】生成默认函数的关键字default&delete(代码演示)
|
1月前
|
安全 算法 编译器
C++中=delete的巧妙用法
C++中=delete的巧妙用法
48 0
|
5月前
|
C语言 C++
C++动态内存管理(new和delete)
C++动态内存管理(new和delete)
82 0
|
2月前
|
算法 安全 C++
C++ Effective Modern Pointer (智能指针模块)
C++ Effective Modern Pointer (智能指针模块)
|
2月前
|
安全 编译器 C语言
深入了解C++:形参、内联、重载、引用、const和指针、new和delete
深入了解C++:形参、内联、重载、引用、const和指针、new和delete
21 1
|
3月前
|
存储 安全 C++
【C++14保姆级教程】lambda 初始化捕获 new/delete 消除
【C++14保姆级教程】lambda 初始化捕获 new/delete 消除

热门文章

最新文章