昨天为了对c#的一些机制做更深的理解,特别用了c++的实现与之作对比。这不,c#没搞出大名堂,c++却揪出了一块烦心的事情:
还是昨天那个代码
原本设计SendPersonByReference函数的功能就是传进一个对象的引用(指针和&引用都算,泛化的引用),改变该引用引用的对象。像上面那样设计好,自我感觉还挺良好,先造个辅存tmp,记录原对象的地址,接着析构delete,再将p指向新new的对象。一切ok!(ps:运行的机子是dell的商用机,vista sp1英文版,vs2008 英文版) 运行结果如下图:
pdpd的回住所,突发奇想,想到再运行下代码,于是在自己的小黑(win7 7000 英文版,vs2005)下balabala的敲好代码再运行,ft程序直接崩溃。。。。。。
刚开始想不通,很郁闷,但是后来仔细想想,却发现自己在c++ 指针上犯了一个特大的煞笔级别的错误:踩到了“栈上动态分配堆内存”的大地雷。
地雷:p指针传入,用p new了一块堆内存,void的返回值。函数体一旦结束,拜拜,指针p被销毁了,那块可怜内存死在荒野无人问津,memory leak。
内存问题先谈到这里,就单单谈功能的实现,其实我原先设计的函数是向下面这样的(更加煞笔):
void SendPersonByReference(Person *p)
{
p->personAge = 555;
p = new Person("Nikki", 10000);
}
运行完出来结果:p指向的对象没变….首先,我总结为内存肯定是泄漏了,p在第二句掉头指向新new的对象。于是我就把代码改成了刚开始上面的代码,一运行,哈哈,在dell机上ok,以为万事大吉!谁知在回家后却发现代码重大的隐患…
仔细分析:
1. 针对最开始的代码,首先就是出现了栈上动态分配堆内存的大忌(详见上面的地雷)
2. 从函数设计和功能上来说,我传入一个对象指针p,其实也只是对指针p的拷贝,函数体中实际在做的操作其实全都是p’
p = new Person("Nikki", 10000);
这一句其实只是把p‘指向新得到的对象,而不是改变了真正需要改变的p。所以,就算你没有memory leak,它的运行的结果也不是你原先设计想要的答案。
3. 应该如何设计呢?
void SendPersonByReference(Person **p)
{
*p = new Person("Nikki", 10000);
}
4. 最后一个疑问
为什么原先的设计在dell的机器上运行的时候,错误的设计却得到了正确的答案,而在别的机器上运行又是错误的呢?
这个问题我也没想特别的明白,windows以及编译器内部的处理机制也不是特别的清楚,估计应该是
void SendPersonByReference(Person *p)
{
Person *tmp = p;
p->personAge = 555;
delete tmp;
// p = new Person("fds",234);
p = new Person("Nikki", 10000);
}
通过delete tmp,我们将p,p‘,tmp共同指向的内存区域“回收”了,p,p’以及tmp成为了传说中的野指针。(事实上它们乱指一气吗?) 。通过p = new Person("Nikki", 10000); p‘指向了新的内存区域,按照在dell上的运行结果反推回去,我们可以知道,新分配的内存其实就是原先被delete的内存区域,不然为何p(不是p’)会指向新开辟的内存区域~。根据我的推断,也就是说编译器以及os将最新回收的内存立即又分配给了新的请求(new new Person("Nikki", 10000) ),具体vista以及编译器的内部实现,水平太差,有待继续深入研究。
为了进一步的验证我的臆测,我修改代码:
void SendPersonByReference(Person *p)
{
Person *tmp = p;
p->personAge = 555;
delete tmp;
p = new Person("damn",1234);
p = new Person("Nikki", 10000);
}
运行结果和前面的运行结果一样,也就是说在函数栈没被destroy之前,p指向了“new Person("damn",1234);”的地址,p‘指向“p = new Person("Nikki", 10000);”指向的地址。当然了,函数体结束以后,遗漏了2块堆内存,一块(new Person("Nikki", 10000);)真的是泄露了,另一块(p = new Person("damn",1234);)歪打正着被原先的实参p指着。
总结:
依靠野指针未确定的行为进行编程是极其有害和邪恶的,我们当然应该避之不及。C++给了程序员足够的信任和自由去操作内存和系统,但也另外给了我们一颗颗隐患的地雷,继续修炼吧devx!以上有错误的地方的,还肯请大牛小牛们指点,俺现在水平就这么点,想到的全写了。止笔于此,该看c#去了…….