38

这是一个困扰我一段时间的问题。我一直认为 C++ 的设计应该使delete运算符(不带括号)即使与new[]运算符一起工作。

在我看来,写这个:

int* p = new int;

应该等同于分配一个包含 1 个元素的数组:

int* p = new int[1];

如果这是真的,那么delete操作符总是可​​以删除数组,我们就不需要delete[]操作符了。

delete[]在 C++ 中引入运算符有什么原因吗?我能想到的唯一原因是分配数组的内存占用很小(您必须将数组大小存储在某个地方),因此区分deletevsdelete[]是一个小的内存优化。

4

7 回答 7

43

这样就可以调用各个元素的析构函数。是的,对于 POD 数组,没有太大区别,但是在 C++ 中,您可以拥有具有非平凡析构函数的对象数组。

现在,您的问题是,为什么不让newdelete表现得像new[]anddelete[]并摆脱new[]and delete[]?我会回到 Stroustrup 的“设计与进化”一书中,他说如果你不使用 C++ 特性,你不应该为它们付费(至少在运行时)。按照现在的方式, a newor的行为将与anddelete一样有效。如果有意义,在运行时会有一些额外的开销(正如 James Curran 指出的那样)。mallocfreedeletedelete[]

于 2008-10-31T03:25:46.290 回答
10

该死的,我错过了整个问题的重点,但我会把我原来的答案作为旁注。为什么我们有delete[]是因为很久以前我们有delete[cnt],即使在今天你写delete[9]or delete[cnt],编译器只是忽略之间的东西[]但编译OK。那时,C++首先由前端处理,然后馈送到普通的C编译器。他们无法将伯爵藏在窗帘下的某个地方,也许他们当时根本想不到。并且为了向后兼容,编译器很可能使用在 之间给出的值[]作为数组的计数,如果没有这样的值,那么他们从前缀中获取计数,所以它是双向的。后来,我们之间没有输入任何内容[],一切正常。今天,我不认为delete[]是必要的,但实现需要这样。

我的原始答案(没有抓住重点):

delete删除单个对象。delete[]删除一个对象数组。为了delete[]工作,实现保持数组中元素的数量。我只是通过调试 ASM 代码仔细检查了这一点。在我测试的实现(VS2005)中,计数被存储为对象数组的前缀。

如果delete[]在单个对象上使用,则 count 变量是垃圾,因此代码会崩溃。如果使用delete对象数组,由于某些不一致,代码会崩溃。我刚刚测试了这些案例!

delete只是删除分配给数组的内存。” 另一个答案中的陈述是不正确的。如果对象是一个类,delete就会调用DTOR。只需在 DTOR 代码和delete对象中放置一个断点,断点就会命中。

我想到的是,如果编译器和库假设所有分配的对象new都是对象数组,那么调用delete单个对象或对象数组就可以了。单个对象只是计数为 1 的对象数组的特例。无论如何,也许我缺少一些东西。

于 2008-10-31T07:50:08.393 回答
9

由于其他人似乎都错过了您的问题的重点,我只想补充一点,几年前我也有同样的想法,但一直无法得到答案。

我唯一能想到的是将单个对象视为数组(不必要的 " for(int i=0; i<1; ++i)" )的额外开销非常小

于 2008-10-31T04:51:54.163 回答
5

添加这个,因为目前没有其他答案解决它:

数组delete[]永远不能用在指向基类的指针上——当你调用时编译器存储对象的数量new[],它不存储对象的类型或大小(正如大卫指出的那样,在 C++ 中你很少支付对于您未使用的功能)。但是,标量delete可以通过基类安全地删除,因此它既可用于普通对象清理,也可用于多态清理:

struct Base { virtual ~Base(); };
struct Derived : Base { };
int main(){
    Base* b = new Derived;
    delete b; // this is good

    Base* b = new Derived[2];
    delete[] b; // bad! undefined behavior
}

然而,在相反的情况下——非虚拟析构函数——标量delete应该尽可能便宜——它不应该检查对象的数量,也不应该检查被删除对象的类型。这使得对内置类型或普通旧数据类型的删除非常便宜,因为编译器只需要调用而不需要::operator delete其他任何东西:

int main(){
    int * p = new int;
    delete p; // cheap operation, no dynamic dispatch, no conditional branching
}

虽然不是对内存分配的详尽处理,但我希望这有助于阐明 C++ 中可用的内存管理选项的广度。

于 2008-10-31T20:32:13.327 回答
4

Marshall Cline 有一些关于这个主题的信息。

于 2008-10-31T03:29:19.620 回答
3

delete []确保调用每个成员的析构函数(如果适用于类型),同时delete删除为数组分配的内存。

这是一个很好的阅读:http ://www.informit.com/guides/content.aspx?g=cplusplus&seqNum=287

不,数组大小不存储在 C++ 中的任何位置。(感谢大家指出这个说法不准确。)

于 2008-10-31T03:27:23.993 回答
1

我对 Aaron 的回答有点困惑,坦率地说我不完全理解为什么需要以及在哪里delete[]需要。

我用他的示例代码做了一些实验(在修正了一些错别字之后)。这是我的结果。错别字:~Base需要一个函数体 Base *b被声明了两次

struct Base { virtual ~Base(){ }>; };
struct Derived : Base { };
int main(){
Base* b = new Derived;
delete b; // this is good

<strike>Base</strike> b = new Derived[2];
delete[] b; // bad! undefined behavior
}

编译和执行

david@Godel:g++ -o atest atest.cpp 
david@Godel: ./atest 
david@Godel: # No error message

delete[]已删除的修改程序

struct Base { virtual ~Base(){}; };
struct Derived : Base { };

int main(){
    Base* b = new Derived;
    delete b; // this is good

    b = new Derived[2];
    delete b; // bad! undefined behavior
}

编译和执行

david@Godel:g++ -o atest atest.cpp 
david@Godel: ./atest 
atest(30746) malloc: *** error for object 0x1099008c8: pointer being freed was n
ot allocated
*** set a breakpoint in malloc_error_break to debug
Abort trap: 6

当然,我不知道delete[] b第一个例子是否真的有效;我只知道它不会给出编译器错误消息。

于 2013-12-04T14:53:27.877 回答