拂晓·明月·弯刀

观望,等待只能让出现的机会白白溜走

  C++博客 :: 首页 ::  :: 联系 :: 聚合  :: 管理 ::
    在Windows编程中,GDI资源的泄露一直是需要引起C++程序员的高度关注,一不小心,就会在函数的中途正常退出或者中途抛出异常退出的地方遗忘掉释放前面申请的资源。本人也曾多次碰到这种问题,查阅了网上的资料,总是不能得到满意的解决。最近看了下boost中的库,才略有收获,也算是抛砖引玉吧。
    要想解决上面的问题,就必须实现资源的自动释放,类的析构函数正好可以满足此要求,就象标准库中智能指针就是这么实现的,但问题在于我们的参数个数,参数类型的不确定性。虽然重载和模板可以解决此问题(这也是我在网上看到的解决方法),但模板类的参数不具备自动推导能力(经传入成员函数参数值推导出模板参数类型),而且过多的模板偏特化也不是我所擅长的,最主要是代码的移植性无法保证。
    本文主要利用的boost中的bind库,觉得仿函数的功能跟自己当前的需求不远了,因为它们的共同点有:
1. 可以接收任意多个模板参数(没有具体验证,至少是9个吧),
2. 可以利用函数对模板参数类型的推导能力,省去了参数类型的指定。
唯一不同的是bind后的仿函数是立即执行,不能具有类的析构函数自动执行的优点。目前需要解决的问题是推迟执行期,也既把operator()函数移到析构函数中执行,这就需要保存boost::bind(....)返回的对象,通过类的构造函数去保存,然后在析构函数中执行operator()就可以了。
    思路是出来了,但问题是boost::bind(...)函数返回的类型不确定,对象通过类模板是可以保存,但类没有自动推导能力,还是无法实现,这里我就利用了boost::any的原理,正好解决了此问题,而且它也可以用于函数的延迟执行。详见以下使用方法:
步骤1: 实现类似于boost:;any的类,主要完成资源的自动释放。实现如下:
//SrcRelease.h头文件
 1#ifndef _SRCRELEASE_INC_
 2#define _SRCRELEASE_INC_
 3
 4class CSrcRelease
 5{
 6public
 7    template<typename T>
 8    CSrcRelease(const T & value)
 9        : m_pHelder(new Helder<T>(value))
10    {
11    }

12
13    ~CSrcRelease()
14    {
15        delete m_pHelder;
16    }

17
18private
19    class IHelder
20    {
21    public:
22        virtual ~IHelder() {}
23    }
;
24
25    template<typename T>
26    class Helder : public IHelder
27    {
28    public
29        Helder(const T & value)
30            : held(value)
31        {
32        }

33        ~Helder() 
34        {
35            held();
36        }

37
38    public// representation
39        T held;
40    }
;
41
42    IHelder* m_pHelder;
43}
;
44
45#endif //_SRCRELEASE_INC_ 
46

步骤2: 下载boost库,因为只用到了boost::bind库,所以无需编译. 将头文件目录加入vs2005中。
步骤3: 客户端调用
//main.cpp
 1#include "SrcRelease.h"
 2#include <iostream>
 3#include <Windows.h>
 4#include <boost/bind.hpp>
 5#include <cassert>
 6
 7void _stdcall InvokeStr(const char* szValue)
 8{
 9    std::cout<<szValue<<std::endl;
10}

11
12bool _stdcall InvokeStr(const char* szValue, int a, int b)
13{
14    std::cout<<szValue<<"\ta: "<<a<<"\tb: "<<b<<std::endl;
15    return true;
16}

17
18int main()
19{
20    //由于API都是_stdcall调用,而vs2005环境都是默认_cdecl,所以需要修改vs2005环境
21    HBITMAP hBitmap=reinterpret_cast<HBITMAP>(LoadImage(NULL, L"test.bmp", IMAGE_BITMAP, 00, LR_LOADFROMFILE));
22    assert(hBitmap!=NULL);
23    CSrcRelease aBitmapRelease(boost::bind(&DeleteObject, hBitmap));
24
25    std::cout<<"Invoke Outer Before"<<std::endl;
26    CSrcRelease aRelease(boost::bind(&InvokeStr, "Invoke Outer After"85));
27
28    {
29        std::cout<<"Invoke Inner Before"<<std::endl;
30        CSrcRelease aRelease(boost::bind(&InvokeStr, "Invoke Inner After"));
31        std::cout<<"Invoke Inner Middle"<<std::endl;
32    }

33
34    std::cout<<"Invoke Outer Middle"<<std::endl;
35    return 0;
36}
 

以上代码在winxp+vs2005下测试通过,如有疑问,欢迎联系: ietj@mail.21cn.com
 
                                                            哓月刀
                                                            2008.3.5
posted on 2009-10-23 14:50 一路风尘 阅读(1121) 评论(1)  编辑 收藏 引用 所属分类: C++技术/工作总结

评论

# re: C++ 资源释放 2009-11-13 01:23 OwnWaterloo
any会使用动态内存,效率比较低。而且,你也不希望看到如下代码:
CSrcRelease ... 产生异常吧?

可以用function<void ()>来保存bind的结果,并在析构函数中调用。
或者,使用Loki::ScopeGuard。

  回复  更多评论
  


只有注册用户登录后才能发表评论。
【推荐】超50万行VC++源码: 大型组态工控、电力仿真CAD与GIS源码库
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理