这个问题一直困扰着我。我一直认为C ++应该设计成“删除”操作符(没有括号),即使使用“new []”运算符也是如此。
在我看来,写下这个:
int* p = new int;
应该等同于分配1个元素的数组:
int* p = new int[1];
如果这是真的,“删除”操作符可能总是删除数组,我们不需要“delete []”操作符。
有没有理由在C ++中引入“delete []”运算符?我能想到的唯一原因是分配数组的内存占用量很小(你必须在某处存储数组大小),因此区分“delete”与“delete []”是一个很小的内存优化。
答案 0 :(得分:37)
这样就可以调用各个元素的析构函数。是的,对于POD数组,没有太大区别,但在C ++中,您可以拥有具有非平凡析构函数的对象数组。
现在,您的问题是,为什么不让new
和delete
表现得像new[]
和delete[]
,并摆脱new[]
和{{1} }?我会回到Stroustrup的“设计和演变”一书,他说如果你不使用C ++功能,你就不应该为它们付费(至少在运行时)。现在的方式是,delete[]
或new
的行为与delete
和malloc
一样高效。如果free
具有delete
含义,则在运行时会有一些额外的开销(正如James Curran指出的那样)。
答案 1 :(得分:9)
该死的,我错过了整个问题,但我会将原来的答案留作旁注。为什么我们删除[]是因为很久以前我们删除了[cnt],即使今天你写了删除[9]或删除[cnt],编译器只是忽略了[]之间的东西但编译好了。那时,C ++首先由前端处理,然后送到普通的C编译器。他们无法在窗帘下面的地方存储计数,也许他们当时甚至都想不到它。为了向后兼容,编译器最有可能使用[]之间给出的值作为数组的计数,如果没有这样的值,那么它们从前缀获得计数,因此它可以双向工作。后来,我们在[]和一切工作之间没有输入任何内容。今天,我不认为“删除[]”是必要的,但实现要求它。
我的原始答案(错过了重点)::
“删除”删除单个对象。 “delete []”删除对象数组。要使delete []起作用,实现将保留数组中的元素数。我只是通过调试ASM代码来仔细检查这一点。在我测试的实现(VS2005)中,计数被存储为对象数组的前缀。
如果在单个对象上使用“delete []”,则count变量是垃圾,因此代码崩溃。如果对对象数组使用“delete”,则由于某些不一致,代码会崩溃。我刚刚测试了这些案例!
“delete只删除为阵列分配的内存。”另一个答案中的陈述是不对的。如果对象是类,则delete将调用DTOR。只需在DTOR代码中放置一个断点并删除该对象,断点就会命中。
我发生的事情是,如果编译器&库假设“new”分配的所有对象都是对象数组,可以为单个对象或对象数组调用“delete”。单个对象就是一个计数为1的对象数组的特殊情况。也许有些东西我不知道了,无论如何......
答案 2 :(得分:6)
由于其他人似乎都错过了你的问题所在,我只想补充说,我在一年前就有同样的想法,而且从未能得到答案。
我唯一能想到的是,将一个对象作为一个数组处理是一个非常小的额外开销(一个不必要的“for(int i=0; i<1; ++i)
”)
答案 3 :(得分:5)
添加此内容,因为目前没有其他答案可以解决这个问题:
数组delete[]
不能在指针到基类上使用 - 当编译器在调用new[]
时存储对象的数量时,它不会存储类型或大小的类型对象(正如David指出的那样,在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 ++中可用的内存管理选项的广度。
答案 4 :(得分:3)
Marshall Cline有一些info on this topic。
答案 5 :(得分:2)
delete []
确保调用每个成员的析构函数(如果适用于该类型),而delete
只删除为该数组分配的内存。
这是一个很好的阅读:http://www.informit.com/guides/content.aspx?g=cplusplus&seqNum=287
不,数组大小不会存储在C ++中的任何地方。(感谢大家指出这个语句不准确。)
答案 6 :(得分:0)
我对Aaron的回答感到有些困惑,坦率地承认我并不完全理解为什么以及需要删除[]的地方。
我用他的示例代码做了一些实验(在修正了几个拼写错误之后)。这是我的结果。 错别字:~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
已删除删除[]的已修改程序
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
当然,我不知道删除[] b是否真的在第一个例子中工作;我只知道它没有给出编译器错误消息。