hur.cn - 华软网

 热门搜索

在大量代码中如何避免忘记释放资源

  作者:未知    来源:网络    更新时间:2011/8/21
原因很简单,大家都应该深有体会。在使用系统资源的时候,比如说打开文件的句柄如果你这次以读写的方式打开了文件在各种错综复杂的调用之中可能就会忘记在哪里将资源关闭了或者在哪里没有关闭
代码量一大就开始晕了,可能就会忘记释放资源,当然几千行的还好,不会出这种问题,一旦上万之后根本不知道在哪里曾经关闭或者曾经打开。
这个肯定和设计有关系,就是说在外面申请的资源的句柄当做参数传入之后一定要在外面释放,而不是在所调用的函数内部释放(比如文件句柄用CloseHandle())。
可是,悲催的我已经写了那么多代码,更悲催的是有的是在函数内部的时候释放的有时候还在外面释放,头疼啊。
毕竟第一次写了那么多的代码,又隔了2个月,回头看,真想推倒了重头在来。
不知道各位兄弟能不能给点设计上的建议,或者推荐基本关于这方面的书籍,其实市面上的书籍都是关于编程语法、库的使用啊,涉及到商用程序的开发,或者程序健壮性以及良好的设计还是比较少的,除非你硬着头皮看源代码,看看别人怎么写的,可是时间来不及。当然我也看到了一些比较好的书比如说《0Bug 商用工程之道》我觉得讲得不错,但是还是讲的还是不够深入!
希望各位给点程序设计方面的建议,特别是大的工程方面的。不尽感谢


---华软 网友回答---
使用类,在析构函数里释放
---华软网友回复---
引用楼主 xizero00 的回复:
原因很简单,大家都应该深有体会。在使用系统资源的时候,比如说打开文件的句柄如果你这次以读写的方式打开了文件在各种错综复杂的调用之中可能就会忘记在哪里将资源关闭了或者在哪里没有关闭
代码量一大就开始晕了,可能就会忘记释放资源,当然几千行的还好,不会出这种问题,一旦上万之后根本不知道在哪里曾经关闭或者曾经打开。
这个肯定和设计有关系,就是说在外面申请的资源的句柄当做参数传入之后一定要在外面释放,而……


这个我在那本商用工程之道中看过,用类的确是可以解决,可是程序已经写好了(那本讲的是互斥的使用经常忘记Unlock),如果用类的话估计要改不少额。真心想问问你们现在做大的项目是怎么解决这个问题的?
---华软网友回复---
用经验指导编程
---华软网友回复---
这个还是看编码习惯吧,特别是在写函数的时候,涉及到申请空间,创建HANDLE的地方,在退出前都进行相关的资源释放就可以了,类的全局变量,在析构的地方也进行必要的检查。
---华软网友回复---
引用 2 楼 xizero00 的回复:
引用楼主 xizero00 的回复:
原因很简单,大家都应该深有体会。在使用系统资源的时候,比如说打开文件的句柄如果你这次以读写的方式打开了文件在各种错综复杂的调用之中可能就会忘记在哪里将资源关闭了或者在哪里没有关闭
代码量一大就开始晕了,可能就会忘记释放资源,当然几千行的还好,不会出这种问题,一旦上万之后根本不知道在哪里曾经关闭或者曾经打开。
这个肯定和设计有关系,就是说在外面申请的资源……


不妨分享一下您的经验,以期一起进步!
---华软网友回复---
在设计前要规划好。如果已写了不少的话,看看后面还有多少要写,如果还有很多的话,可以从现在就按新的写,前面的找时间再改。
---华软网友回复---
引用 4 楼 dream238 的回复:
这个还是看编码习惯吧,特别是在写函数的时候,涉及到申请空间,创建HANDLE的地方,在退出前都进行相关的资源释放就可以了,类的全局变量,在析构的地方也进行必要的检查。

您所说的退出是指将资源用类封装好的那个类析构函数吗?还是指什么?
如果不封装的话,是不是还有其他的什么办法?

---华软网友回复---
引用 6 楼 happytonice 的回复:
在设计前要规划好。如果已写了不少的话,看看后面还有多少要写,如果还有很多的话,可以从现在就按新的写,前面的找时间再改。


已经写好了。如果要想程序健壮的话,肯定要推倒重来。
其实我一直有这样一个疑问,那就是,真正的商用程序究竟有哪些设计上的原则,我也一直很好奇商业代码究竟是什么样子的,想一睹个究竟。

---华软网友回复---
引用 7 楼 xizero00 的回复:
您所说的退出是指将资源用类封装好的那个类析构函数吗?还是指什么?
如果不封装的话,是不是还有其他的什么办法?

额。。。应该是针对局部变量,在所属的函数执行返回之前或者变量作用域无效前检查。
---华软网友回复---
引用 9 楼 dream238 的回复:
引用 7 楼 xizero00 的回复:

您所说的退出是指将资源用类封装好的那个类析构函数吗?还是指什么?
如果不封装的话,是不是还有其他的什么办法?

额。。。应该是针对局部变量,在所属的函数执行返回之前或者变量作用域无效前检查。


我想问您一下,在您做的项目中,您是怎么解决这个问题的,打个比方,面对多次调用打开的文件句柄
而且还用到了多线程,您是怎么解决这个忘记释放句柄的问题的?要知道代码一多,记性就不好了,╮(╯▽╰)╭丢三落四的是常有的事情。
---华软网友回复---
引用 10 楼 xizero00 的回复:
引用 9 楼 dream238 的回复:

我想问您一下,在您做的项目中,您是怎么解决这个问题的,打个比方,面对多次调用打开的文件句柄
……


网上有些查找内存泄露的方法,可以搜索借鉴下,参考设置后,在DEBUG调试正常退出时,若有内存泄露,会输出相关代码所在行数的。然后根据行数检查代码尽量去改就好了。
更重要的,还是形成边写边检查的习惯,在完成一个函数,一个类,一个功能的时候,都先检查下代码。
---华软网友回复---

引用 11 楼 dream238 的回复:
引用 10 楼 xizero00 的回复:

引用 9 楼 dream238 的回复:

我想问您一下,在您做的项目中,您是怎么解决这个问题的,打个比方,面对多次调用打开的文件句柄
……


网上有些查找内存泄露的方法,可以搜索借鉴下,参考设置后,在DEBUG调试正常退出时,若有内存泄露,会输出相关代码所在行数的。然后根据行数检查代码尽量去改就好了。
更重要的,还是形成边写边检……

嗯,好的,谢谢你的建议!
---华软网友回复---
不行的时候来一句exit(-1)闪人
---华软网友回复---
    很多人总是说:书过千遍不如手敲一遍。其实,要是在下手前没有大师们的杰作作为墨水的话,想你也只能对着显示器发愣。(为避免被喷,本人墨水很有限,只是想发表内心的想法然后对楼主有所帮助)
    每一本好书除了分享强大的开发技术外,对于开发过程中的必须注意的问题也是苦口婆心。个人深受这些书籍的影响,在开发过程中也很注重养成良好的开发习惯。每次用到数组,必须memset或者ZeroMemory初始化,什么时候new、open、push只有在delete、close、pop了心中的担子才敢放下。每次用到系统资源的时候都会如履薄冰,实在无法记忆时,那就用智能指针、有自动释放机制的函数。当然,在每次增加代码时不能确保一次编译成功,基本上是以几十行或者一个函数为编译周期基准。个人觉得一个大工程是在代码规范化的前提下壮大起来的,假如这个工程制造了难以释放系统资源的问题,那只能归咎于本身的设计问题。怎么解决,多看好书、多研究别人的设计风格(VS里边就有很多代码风格是值得我们去挖掘和研究的)最后一点不能避免的是,你必须经历不断犯错的过程!
---华软网友回复---
随时重构
---华软网友回复---
去搞一个内存泄露工具使用下~~

http://download.csdn.net/source/3502898
---华软网友回复---
我一般碰到需要释放的资源在新建的时候同时把释放的语句也写好。
---华软网友回复---
引用 14 楼 gibsonboy 的回复:
            很多人总是说:书过千遍不如手敲一遍。其实,要是在下手前没有大师们的杰作作为墨水的话,想你也只能对着显示器发愣。(为避免被喷,本人墨水很有限,只是想发表内心的想法然后对楼主有所帮助)
           每一本好书除了分享强大的开发技术外,对于开发过程中的必须注意的问题也是苦口婆心。个人深受这些书籍的影响,在开发过程中也很注重养成良好的开发习惯。每次用到数组,必须mem……

谢谢你的建议,很中肯!的确,在源代码后的背后隐藏的是设计者的巨大的心血,唯有查看一行行源代码才能够最终学到精髓。我们编程不能仅仅停留在表面。
说到设计,不知道您能否介绍一下在设计方面需要注意的东西,譬如说函数,类,函数的参数以及功能模块是怎么分割的以什么为基准,遵循什么设计原则,该怎么处理一些在实际运行中所碰到的异常以及如何从异常中恢复。
谢谢您能够这么认真地回答(*^__^*) 
---华软网友回复---
引用 16 楼 doctorwing 的回复:
去搞一个内存泄露工具使用下~~

http://download.csdn.net/source/3502898


不仅仅是内存泄露的问题。。。比如说句柄忘记释放了该怎么办,如何避免掉这些忘记如何编写合格的正确的商用工程代码
---华软网友回复---
引用 17 楼 a7320760 的回复:
我一般碰到需要释放的资源在新建的时候同时把释放的语句也写好。


这个建议很好,不过如果在很大的项目中,你知道在哪里把释放的语句写好吗,比如你在这个函数申请的,却到了另外一个函数释放,而另外一个函数还没有写,只是在构思中怎么解决呢
---华软网友回复---
释放函数?
---华软网友回复---
引用 15 楼 heksn 的回复:
随时重构


重构也是需要付出代价的,如果准备随时重构,有些东西还是重复劳动的
---华软网友回复---
引用 13 楼 rrrfff 的回复:
不行的时候来一句exit(-1)闪人


呵呵,这太不切实际啦
---华软网友回复---
引用 21 楼 etyanyan 的回复:
释放函数?


是释放资源。
就是如何避免忘记释放资源。
---华软网友回复---
这个事情有个简单的小技巧,就是象程序流程结构一样,写{时,接着也把}写出来,然后在中间写代码。
同样,创建需要资源的对象时,就把释放写出来(当然,往往并不是在同一个程序片断里,但你一定应该知道是在哪里)。因为不管什么原因,程序可能要写几天或几个月,一开始只用脑子记忆的事情,过后很可能忘了,即使想着做也还要回头去检查(就象楼主现在这样),还不如随时做好。

至于楼主已经写成之后现在的情况,就不好办了,也所以说养成好的编程习惯才显得重要。
---华软网友回复---
更悲催的是有的是在函数内部的时候释放的有时候还在外面释放
===========================================================
用安全释放的方法:
if(p)
{
delete p;
p=NULL;//养成良好的习惯,就算你确定这个P以后再也不用了,也不能让它成为野指针
}

已经完成的代码就没办法了,只能自己一个个search,看下有没有相应的release或deleteobject或delete了。最根本的办法就是养成好的编码习惯,写了一行new或createfile之类需要扫尾工作的代码后,就马上写一句相应的delede或closehandle之类的,如果暂时不确定放哪里合适,也要先好这一句放在最后先。这样最省心。
---华软网友回复---
你要释放的是什么资源?

HANDLE?申请的内存?还是?

重载new/delete,在Debug情况下记录调用/删除次数并相减,不为0肯定泄漏。

还有就是使用内存池,在程序退出的时候可以assert(已使用内存==0)来确定是否释放完毕。

如果是系统对象的HANDLE之类的可以保存一个记录结构,因为一般他们都可以调用CloseHandle释放,重复调用不会异常,而且在发布版本中可以去掉。


或者代码书写格式像这样

C++">
HANDLE xxx = GetXXXXX();
{
    //do sth here
}
CloseHandle(xxx);


清晰明了
---华软网友回复---
这个东东要靠自己养成良好的编程习惯,不用的内存及时释放
---华软网友回复---
C++开发,自己管理和释放资源是必须的、基本的素质,否则你应该用C#或者Java更合适。
另外,《0Bug 商用工程之道》是本错误百出的烂书,具体可查看网上有关的评论。
---华软网友回复---
只要养成如下的一种习惯, 保证你再也不必为释放内存, 释放系统资源而担心.
写代码前, 思清楚整个数据流程.
写代码时, 每个变量, 注释写明其作用和意图, 每个创建的变量, 必须注明是内部使用, 还是创建给外部使用, 并注释清楚应该在哪里进行释放.
只要勤写注释, 一切都不成为问题.
---华软网友回复---
引用 18 楼 xizero00 的回复:
引用 14 楼 gibsonboy 的回复:

很多人总是说:书过千遍不如手敲一遍。其实,要是在下手前没有大师们的杰作作为墨水的话,想你也只能对着显示器发愣。(为避免被喷,本人墨水很有限,只是想发表内心的想法然后对楼主有所帮助)
每一本好书除了分享强大的开发技术外,对于开发过程中的必须注意的问题也是苦口婆心。个人深受这些书籍的影响,在开发过程中也很注重养成良好的开发习惯。每次用到数组,必须……

谢谢楼主的有答必回,“函数,类,函数的参数以及功能模块是怎么分割的以什么为基准,遵循什么设计原则”。关于类和函数及功能模块的基准和遵循的原则,在下资历尚浅,不敢轻言什么基准和原则。一切都是在每次开发后的经验总结而已。函数和类的设计相信有一本书非常有发言权《Effective C++》,功能模块怎么分割,这个问题好广有得根据个人的原因和需求,恐怕我回答不上来了。但是我可以分享一下自己的浅薄经验:界面的显示和功能的实现调用必须分开,功能的实现不允许影响到界面的体验效果。在数据交换频繁的程序,类与类之间的数据交换必须封装和打包起来,避免大数据的类间传输。(可以用一个类来包装,让我们的类都继承于它。我们都知道C++有强大的指针转换),在得到这个类数据用指针转换取出数据,个人感觉指针最大的好处就是可以在数据传输时只传一个指针(地址),而只要有这个指针,就可以拿到你想要的数据。类与类之间的数据传送桥梁,个人浅见认为以发送消息为桥梁,我们都知道windows是个以消息和事件为机制的系统,不妨定义标准的自定义消息,跨类发送消息,有父子关系类的消息就更方便了。个人觉得发送消息减少了类成员的创建及函数调用时临时栈的创建等等更多我不知道的系统开销。
"怎么处理一些在实际运行中所碰到的异常以及如何从异常中恢复"这个除了我们开发时所能捕捉到的异常,我想更多的是靠开发经验累积起来的,很多东西只要设计的原理是正确明白的,都可以找出问题根源加以恢复的。异常问题又是一个很广的问题,我也不好回答了。我觉得你应该不喜欢听我说“抛异常”、“断言”之类的建议吧。
另外推荐楼主一直强调的设计模式,有一本好书,有兴趣看看吧。Alan Shalloway的
《Design Patterns Explained》
---华软网友回复---
养成一个良好的习惯,分配空间的时候 就在空一行施放空间。
---华软网友回复---
把操作面向对象化。 封装成单独类。 比如文件io就写个CFile 对象释放时。调用析构. 里面判断文件句柄是否存在。在,则关闭文件。 这样就完美解决了 只打开,忘关闭的句柄泄露。

---华软网友回复---
使用类封装需要(配对)进行的后处理操作,避免在异常发生时完成必要的清理工作
比如下面的例子使用AObjCounter的构造和析构函数对AObj对象进行AddRef和ReleaseRef操作,这样即使中间obj.DoOperation()抛出异常,obj.Release()也会被正确地调用

#include <iostream>

class AObj
{
public:
AObj()
{
std::cout << "AObj constructor called" << std::endl;
}
~AObj()
{
std::cout << "AObj destructor called" << std::endl;
}

void AddRef()
{
std::cout << "AObj AddRef() called" << std::endl;
}
void ReleaseRef()
{
std::cout << "AObj RemoveRef() called" << std::endl;
}

void DoOperation()
{
std::cout << "AObj DoOperation() called" << std::endl;
throw "";
}
};

class AObjCounter
{
public:
AObjCounter(AObj &obj)
: m_obj(obj)
{
m_obj.AddRef();
}
~AObjCounter()
{
m_obj.ReleaseRef();
}

private:
AObj &m_obj;
};

class ATest
{
public:
void TestFun()
{
AObj obj;
AObjCounter c1(obj), c2(obj);
// obj.AddRef();
// obj.AddRef();
obj.DoOperation();
// obj.ReleaseRef();
// obj.ReleaseRef();
}
};

int main()
{
try
{
ATest at;
at.TestFun();
}
catch (...)
{
}
return 0;
}

---华软网友回复---
可以试着用宏定义来改成间接申请来检查,如果觉得影响效率,在发布版本可以替换回去。
---华软网友回复---
哈哈,最好的方法是在分配资源的下一句立即写上回收资源的代码...
---华软网友回复---
shared_ptr 与其它资源

---华软网友回复---
一到分配资源的语句,偶的脑袋就一直挂念着、挂念着、挂念着........一直到用完该资源。释放,哈哈。
或者直接写
if(分配成功)
{

释放
}

且最后加上:if(该资源未释放) 则释放
---华软网友回复---
引用 25 楼 theforever 的回复:
这个事情有个简单的小技巧,就是象程序流程结构一样,写{时,接着也把}写出来,然后在中间写代码。
同样,创建需要资源的对象时,就把释放写出来(当然,往往并不是在同一个程序片断里,但你一定应该知道是在哪里)。因为不管什么原因,程序可能要写几天或几个月,一开始只用脑子记忆的事情,过后很可能忘了,即使想着做也还要回头去检查(就象楼主现在这样),还不如随时做好。

至于楼主已经写成之后现在的情况,就……


嗯,对的就像写括号一样。^_^
---华软网友回复---
引用 38 楼 thomtadie 的回复:
一到分配资源的语句,偶的脑袋就一直挂念着、挂念着、挂念着........一直到用完该资源。释放,哈哈。
或者直接写
if(分配成功)
{

释放
}

且最后加上:if(该资源未释放) 则释放


谢谢您的回答,不过如果调用复杂了就不会这么简答了
---华软网友回复---
引用 37 楼 zyq5945 的回复:
shared_ptr 与其它资源


智能指针还是可以解决一些内存释放的问题的,谢谢你
---华软网友回复---
配对写法啊!
---华软网友回复---
引用 42 楼 wo65432519 的回复:
配对写法啊!

不是那么简单啊,要是配对写法那么简单就不会出现用类去封装了
---华软网友回复---
引用 31 楼 gibsonboy 的回复:
引用 18 楼 xizero00 的回复:

引用 14 楼 gibsonboy 的回复:

很多人总是说:书过千遍不如手敲一遍。其实,要是在下手前没有大师们的杰作作为墨水的话,想你也只能对着显示器发愣。(为避免被喷,本人墨水很有限,只是想发表内心的想法然后对楼主有所帮助)
每一本好书除了分享强大的开发技术外,对于开发过程中的必须注意的问题也是苦口婆心。个人深受这些书籍的影响,在开发过……


好建议,已经在看More Effective C++了,设计模式暂时还没时间去看,感觉每次做项目都会收获很多,重构了不少代码,第二次重写的时候就不会再犯以前的错误了,也许这就是所谓的慢慢地进步吧,谢谢您的回答,肺腑之言哪!
---华软网友回复---
引用 29 楼 r3000 的回复:
用C++开发,自己管理和释放资源是必须的、基本的素质,否则你应该用C#或者Java更合适。
另外,《0Bug 商用工程之道》是本错误百出的烂书,具体可查看网上有关的评论。


这里不谈论书的好坏,作者写这本书肯定有他想侧重表述的东西,看书不一定要盯着坏的看,吸收其精华,去其糟粕,特别是渴望学到商用工程的一些经验的童鞋们的,我想这本书至少说能够聊以慰藉吧,期待更好的介绍这方面的书籍!
---华软网友回复---
析构函数呀,
在或者用C# java  哈哈
---华软网友回复---
1)基本上,不可能避免
2)如果想避免,请自己构造一个垃圾回收程序,哈
---华软网友回复---
多用类(在析构函数中释放资源),多用用智能指针.
---华软网友回复---
最重要的是要形成良好的编程的习惯!
---华软网友回复---
推倒吧

我都是分配释放一起写      
华软声明:本内容来自网络,如有侵犯您版权请来信指出,本站立即删除。