令我震惊的是,以下代码在我的计算机上运行正常,即以正确的顺序构造和破坏了二维数组的所有25个对象。

#include <iostream>

class C {
public:
  C() { std::cout << '+' << this << "\n"; }
  ~C() { std::cout << '-' << this << "\n"; }
};

typedef C T[5][5];

int main() {
  void *t = new T;
  delete (T *) t;
  return 0;
}

这确实是根据标准分配和取消分配多维数组的有效方法吗?是否实际上需要运算符newdelete以这种方式递归检查多维数组类型?

最佳答案

该代码不正确,因为:

  • delete运算符的子表达式没有正确的类型。
  • 由于分配的类型是数组类型,因此必须使用delete[]语法。

  • 正确的代码是:
    // C and T as in the OP.
    
    int main() {
        C (*t)[5] = new T; // or just: auto t = new T;
        delete [] t;
    }
    

    首先,new T的类型是C (*)[5],而不是T*(或C*等)[expr.new] / 5:



    delete[]的子表达式中必须使用相同的类型,并且由于分配的对象是数组,因此必须使用delete[]运算符。 [expr.delete] / 2:



    特别是,如果将您的new表达式产生的指针值强制转换为其他类型(例如T*),则该类型的指针值不是由new表达式产生的指针值。除了删除单个多态类对象的说明外,给deletedelete[]的指针的类型必须与new-expression的类型相同。

    就初始化和销毁​​而言:无论如何创建数组对象,初始化数组对象都涉及初始化该数组的每个元素。当您有一个数组数组时,这将递归地应用。因此,表达式new T创建并初始化了一个C[5][5]数组对象,这意味着初始化了五个C[5]对象,而每个初始化都意味着初始化了五个C对象(总共进行了25个C构造函数调用)。

    而且,无论如何创建数组对象,销毁该数组对象的有效方法都涉及以相反的顺序销毁该数组的每个元素。因此,delete [] t;语句破坏了第一个元素为*t的整个数组-这意味着破坏了C[5]类型的所有五个元素,而每个破坏都意味着破坏了五个C对象(总共25个~C析构函数调用)。

    请注意,编译器从t的类型知道数组元素的类型为C[5],但是它没有指定数组的主要大小。如果*t实际上是例如C[8][5]数组的第一个元素,则相同的表达式将有效。在幕后,许多编译器将通过在由new-expression分配的任何数组旁边存储一定数量的数组元素来解决此问题,以便以后的delete-expression可以确定需要销毁多少个元素。

    关于c++ - C++:delete [] [] ing多维数组,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/50051692/

    10-11 23:20
    查看更多