zoukankan      html  css  js  c++  java
  • 读书笔记 effective c++ Item 16 成对使用new和delete时要用相同的形式

    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>。

  • 相关阅读:
    MD5加密 + 盐
    SQLite数据库--C#访问加密的SQLite数据库
    SQLite问题笔记
    微信开发--Two.菜单生成
    NOIP2018游记(更新完毕)
    HNOI2019 游记
    JXOI2017-2018 解题报告
    网络流20+4题解题报告(已更前20题)
    CodeForces528A (STLset)
    CodeForces 140C New Year Snowmen(堆)
  • 原文地址:https://www.cnblogs.com/harlanc/p/6422351.html
Copyright © 2011-2022 走看看