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