`
RednaxelaFX
  • 浏览: 3020442 次
  • 性别: Icon_minigender_1
  • 来自: 海外
社区版块
存档分类
最新评论

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

    博客分类:
  • C++
阅读更多
刚考完大软,心里还在郁闷,随便发点牢骚吧……

昨天同学考C++的时候还有一题,是找出程序错误的题。代码大概是这样:
class A {
    int i;
};

class B {
    A* p;
public:
    B() { p = new A; }
    ~B() { delete p; }
};

void foo(B b) {
    // ...
}

int main() {
    B b;
    foo(b);
}

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

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

其实应该挺明显的,但昨天被同学问到的时候我却没能一眼看出来。郁闷啊。
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++是门好语言(嗯
分享到:
评论
6 楼 RednaxelaFX 2008-05-19  
顺带一提……这几天连续的都是考试,我从上个星期三到昨天没有一天是没有考试的;今天一直到后天还继续。所以上网时间没保证撒……T T
5 楼 RednaxelaFX 2008-05-19  
C++里,一个类的对象实例默认是可以被复制的,复制会在:
(1)以复制来初始化时:
SomeClass c = someInstance; // initialization by copy

(2)以复制来赋值时:
someInstance = someOtherInstance; // copy by assignment

默认提供的复制机制是将源对象里的每个成员都原封不动的复制到目标对象中;形象点说就是把源对象的每一bit都直接搬到目标对象上。这种复制方式也被称为浅拷贝(shallow copy)。
但我们不总是想允许这种浅拷贝,因为会带来潜在的安全问题(见上面)。当需要提供与默认复制机制不一样的行为时,我们可以
对(1):提供拷贝构造函数;
对(2):提供operator =的重载。
class SomeClass {
    // ...
    SomeClass( const SomeClass& ); // copy constructor
    operator =( const SomeClass& ); // copy assignment
};

另外,当我们希望禁止对象的复制时,可以提供私有的拷贝构造函数和operator =的重载。

可以参考The C++ Programming Language, 3rd Edition的10.2.5 Copying Class Objects [class.default.copy]和10.4.4.1 Copying Objects [class.copy]。
我在几天前发的一篇日记里写的那个Set的例子里也有拷贝构造函数和operator =重载的例子,对这个概念不熟悉的话可以看看。

不过明大肯定对这种概念比我熟悉多了,再怎么说平时写的C++代码都比我多多了,我懒啊不想用C++……
4 楼 Lighting 2008-05-17  
=  =!

貌似是叫拷贝构造函数吧~~~

PS:FX大长期不在啊……
3 楼 Lighting 2008-05-16  
啊……晕了……手残了!~!打少了关键的字了……

这样在foo调用结构后析构的b跟main里面的b是同的对象了……

应该是

这样在foo调用结构后析构的b跟main里面的b是不同的对象了……


(为啥没得编辑功能…… =_,= )
2 楼 Lighting 2008-05-16  
哦呀……像lwwin说的那样按引用传递的话的确是没问题~~~

看到这帖,刚开始我也很郁闷~~把b对象以参数形式传进去不会导致B的构造调用么,反汇编看结果才发现,原来只是进行简单的赋值操作而已……然后再写个B的构造
B(const B &obj)
{
     p=new A;
}
有这个构造的话在进行
B b;
foo(b)的时候,进入foo作用域后就会执行B(const B &obj)构造了~~~这样在foo调用结构后析构的b跟main里面的b是同的对象了……

其实我也觉得很奇怪,为啥有B(const B &obj)在的时候才会进行构造,没它在的话只是进行简单的赋值……
等FX大的解释!!!
1 楼 lwwin 2008-05-16  
void foo(B& b) {...} ???

相关推荐

Global site tag (gtag.js) - Google Analytics