delete和delete[]

 
古城
@ 2009.09.06
分类:技术笔记
点击:1079  评论:0
 
 
 

  之所以想到这个话题,是前段时间一次讨论中提到的new[]一个简单类型的数组,能否用delete而不是delete[]释放的问题。因为明确说了不是类对象,所以这里可以不考虑数组对象析构时的错误,那么这样做可不可以呢?

  有2种声音:严格遵守C++标准,保持好习惯;不要生搬经典,要自己实践。

  为了弄清这个问题,不得不引入一个词“未定义行为”(undefined behavior)。

  In computer science, undefined behavior is a feature of some programming languages — most famously C. In these languages, to simplify the specification and allow some flexibility in implementation, the specification leaves the results of certain operations specifically undefined.

  ——Wikipedia (http://en.wikipedia.org/wiki/Undefined_behavior)

  对于未定义行为,C++标准没有明确规定编译器们应该怎么做,那么执行的结果就是不可预料的。对于上面那个new[]完用delete释放的问题,编译器会做什么呢?不知道,因为C++标准没规定他做什么。夸张点说,即使是编译出来的程序把硬盘格式化了,也不能说这个编译器是个不符合C++标准的——它依然可能是一个完全符合C++标准的优秀编译器。

  下面就用实际例子来试试吧。

int main(int argc, char* argv[])    
{
  while (1)
  {
  int* p=new int[1024*1024];
  delete p; //没有用delete[] p;
  }
  return 0;
}

  上面这段程序在VS下编译,执行了很久,从任务管理器里没发现有什么异常。不过这样就算是没问题了么?

  先换个问题看看,很多C语言启蒙教材里的程序里main()的返回值是void型的:

void main()    
{
}

  这样的程序,大家很习惯了,因为在微软的编译器里,这样是完全可以的。不过有没有试过在LINUX下GCC编译器或者更多其他编译器里试过编译一下呢?很多是会报错的!其实,ANSI C明确规定了main()就是返回int的,void main()的用法是错误的。如此来说,在VS下能编译并正常运行的,就一定是正确的么?

  上面只是个不符合标准的例子,而更多问题,则出现在 undefined behavior 上。正是因为C++标准中没有定义它,编译器没有责任说必须怎么做,[不同编译器] 或者 [同一编译器的不同版本] 或者 [同一编辑器同一版本在使用不同编译选项时] 都可能会有不同执行结果。不相信么?那么事实说话,看个程序吧。

#include <iostream>    
using namespace std;
int main(int argc, char* argv[])
{
  int x=0;
  x=(x++)+(++x)+(x++)+(++x);
  cout<<x<<endl;
  return 0;
}

  VC 6.0下编译,Debug版本输出7,release版本呢?却是10!为什么呢?其他编译器也许会有更多不同答案。

  另一个程序:

#include <iostream>    
using namespace std;
int main(int argc, char* argv[])
{
  int x=1;
  x=(x++ * ++x + x-- * --x);
  cout<<x<<endl;
  return 0;
}

  VC 6.0下编译Debug版本输出5,VS 2003下Debug版本输出2。

  同时微软的编译器出来的结果都各种各样,就更不用说其他公司更多的编译器了。肯定还有更多答案,不过结果本身没有意义,C++标准没有定义这种行为,对于这种undefined behavior,编译器爱怎么做都行,而我们能做的,是避免这种情况出现。“有实力的C++程序员能以最佳状态避开未定义行为。”

  回到最初new[]与delete配对的问题。C++标准里规定了new\delete要配对、new[]\delete[]要配对,并没有说new[]与delete配对之后会做些什么,也就是一种未定义行为,会发生什么无法知道。即使当前某个编译器编译是正确的,至于以后的某个版本是否仍然正确也无法知道。何况C++中可以对new\delete、new[]\delete[]重新定义,谁能保证做了些什么,会有什么不同呢?

  所以最保险、最规范的做法还是,严格遵守C++标准,坚决避免一切undefined behavior。

 
 
 
上一篇:
 
 

本文评论

 
 

发表评论

你的评论
← 填你的昵称
以下内容非必填,可根据需要填写
← 可以展示在你的评论上方
← 不会在页面展示
← 不会在页面展示
← 只给我看?勾选上
这是一个别人称之为角落的世界
幸而,它的确是我的世界