确定性析构在有指针的环境下的麻烦之处

确定性析构在有指针的环境下的麻烦之处

来自http://rednaxelafx.iteye.com/blog/372310

刚考完大软,心里还在郁闷,随便发点牢骚吧……

昨天同学考C++的时候还有一题,是找出程序错误的题。代码大概是这样:
Cpp代码  收藏代码
  1. class A {  
  2.     int i;  
  3. };  
  4.   
  5. class B {  
  6.     A* p;  
  7. public:  
  8.     B() { p = new A; }  
  9.     ~B() { delete p; }  
  10. };  
  11.   
  12. void foo(B b) {  
  13.     // ...  
  14. }  
  15.   
  16. int main() {  
  17.     B b;  
  18.     foo(b);  
  19. }  

你看出问题在什么地方了么?

============================================================================

其实应该挺明显的,但昨天被同学问到的时候我却没能一眼看出来。郁闷啊。
foo()的参数是将B复制传递过去的。于是foo()里的参数b在离开foo()的作用域时会被析构,里面的成员p指向的A实例就一起被析构了。然后在main()里的b在离开main()时析构就会尝试再次delete掉同一个A的实例,引发错误。

So what am I trying to say? 这普通的C++毫无疑问是有确定性析构的——局部变量在离开其作用域时就会被析构;显式使用delete时也可以完成析构。正是因为这样,无意中造成的浅拷贝会让引起一些意想不到的问题,就像上面的代码那样。
为了应对这些问题,我们才有了烦琐的idioms,例如:
1、delete之前先检查指针是否为null;(然而不小心用错delete与delete[]运算符的话还是很糟糕)
2、尽量不对复合类型对象直接使用值传递给参数——可以用引用传递,也可以传指针,以减少复制(但如果不想让参数的值被改变怎么办呢?我们有const修饰符,也有拷贝构造函数和operator =的重载……自己实现深拷贝吧)
3、为了避免内存泄漏,我们可以采用RAII……呃
……

具有RAII的可能性是好是坏我觉得还可以一议,像C#或者Java就没办法用RAII,有些人也会说很不爽什么的。但是内存管理到底是应该留给 程序员做还是应该让运行时解决掉,这毫无疑问是取决于程序所在的层次:高层的应用应该尽量避免涉及这些细节,一方面不容易出错,另一方面也减轻了程序员的 负担。不然我们就得记下一堆idioms,多到能出一整系列的几本书来描述的量,才能写出正确的程序了……

以上纯属吐槽 =v=
C++是门好语言(嗯
============================================================================
确实容易出问题,记下了。
posted @ 2011-12-03 13:24  madonion  阅读(536)  评论(0编辑  收藏  举报