王朝网络
分享
 
 
 

资源释放

王朝vc·作者佚名  2006-01-08
宽屏版  字体: |||超大  

资源释放

问题

正如软件可以抽象为信息采集、信息处理和信息输出一样,对于单个函数,同样可以抽象为资源申请、资源使用和资源释放。这些资源包括内存、网络连接、文件局柄等。就象以下代码所示(为简化代码,忽略了错误处理):

int TextFileLines(const char* szTxtFile)

{

//declare arguments

HANDLE hFile;

HANDLE hMap;

char* lpBuf;

int cbLines;

//acquire resources

hFile=CreateFile(szTxtFile,GENERIC_READ,0,0,OPEN_EXISTING,0,0);

hMap=CreateFileMapping(hFile,NULL,PAGE_READONLY,0,0,NULL);

lpBuf=(char*)MapViewOfFile(hMap,FILE_MAP_READ,0,0,0);

//use resources,calculate number of lines in buffer

cbLines =CalcLines(lpBuf);

//clean up resources

UnmapViewOfFile(lpBuf);

CloseHandle(hMap);

CloseHandle(hFile);

return cbLines;

}

然而,资源的释放却远不是想象中的容易,就像上面的代码中所示,所有的资源看似都已经释放,但实际上却隐含着许多的问题:

1、如果在CalcLines函数中发生异常,在此之后的所有代码都无法运行,资源发生泄漏。

2、随着代码的增加,我们必须在函数的每一个退出点上明确地进行各个资源的释放工作。

3、随着代码的增加,非常容易忘记进行资源的释放工作。

4、由于各系统资源间存在一定的内在关系,如某一资源依赖另一资源的情况,释放多个系统资源时应遵循一定的顺序,即最后申请的资源需要最先释放。

显然,我们需要一种机制,自动解决这些问题。

解决

C++的类机制是解决这些问题的自然选择。我们可以在类的构造函数中登记申请的系统资源,在类的析构函数中释放系统资源(就像auto_ptr一样)。首先,C++语言确保在离开函数的作用域时,包括因发生异常而导致的中断,对应的局部对象都会得到正确的析构,这样就保证了资源的申请和资源的释放必然会成对出现,不会发生资源的泄漏。其次,C++语言确保在局部对象的析构过程中,类析构顺序与类构造顺序相反,即最后声明的局部对象会最先得以析构,这样就确保了资源的释放顺序。最后,我们建立一个编码约定,即在申资源成功后,马上进行资源的释放登记工作,这样就解决了资源释放的遗忘问题。

确定采用类机制后,针对每种具体资源,我们不难设计出一个具体的类,实现系统资源的自动释放,就像MFC中的类一样。但这样容易导致类数量增多、代码膨胀、名字空间得到污染。我们需要一个更轻量级的、更通用的机制。类模板便是用来解决这种问题的方法。

然而,还有许多问题。首先,针对不同的资源,对应的资源释放函数的原型、参数数量和参数类型都各不相同,这就需要数量可变的模板参数、数量可变的函数参数,但遗憾的事,C++中不存在“数量可变的模板参数”,因为参数类型不同,也无法使用“数量可变的函数参数”。我们只能凭借经验,确定有限个数的参数,本文约定为4个。其次,C++中不允许同名而参数个数不同的模板类存在,既不存在模板类重载机制,我们只有针对不同的参数数量,分别命名为Func1、Func2…,但那样对于用户而言,会带来很大的麻烦。我们需要重新封装为统一的名字。

实现

按照以上思路,我们的第一次定义可能是这样(针对一个参数):

template<typename PFN,typename ARG1>

class CFuncPack1

{

public:

CFuncPack1(PFN pfn,ARG1 arg1) : m_pfn(pfn),m_arg1(arg1) {}

~CFuncPack1()

{

m_pfn(m_arg1);

}

PFN m_pfn;

ARG1 m_arg1;

};

其调用方法如下:

int main()

{

HANDLE handle=CreateFile(…);

CFuncPack1< BOOL (WINAPI*)(HANDLE),HANDLE > aFunc(CloseHandle,handle);

return 0;

}

以上便实现了文件局柄资源的自动释放。但从以上客户的调用方式来看,需要用户明确指定模板类的参数类型,尤其是第一个参数为函数指针,指定其类型就像一场恶梦。同时,用户还需根据参数数量的不同明确指定不同的模板类。那么有没有更简单的方法,让客户调用时更简单,就像以下的方式一样呢。

int main()

{

HANDLE handle=CreateFile(…);

CfuncPack aFunc(CloseHandle,handle);

return 0;

}

答案当然是肯定的。首先,我们可以使用一个统一的包装类CFuncPack来封装不同参数的模板类CFuncPack1、CFuncPack2…。虽然C++中不允许同名而参数个数不同的模板类存在,但允许存在同名而参数个数不同的模板函数存在,即模板函数重载,包括模板构造函数,在模板函数中生成不同的模板类。其次,在模板函数的调用过程中,用户可以使用C++语言提供的模板参数推导而无需手工指定参数的具体类型。最终的实现如下(针对一个参数):

class CFuncPack

{

//base class

struct InFuncPackBase

{

virtual void DirectCall(BOOL bClear=true)=0; //Manual Call

virtual ~InFuncPackBase(){}; //virtual destructor

virtual void clear()=0; //reset

};

//template class of one parameter

template<typename FuncType,typename ArgType1>

struct InFuncPack1 : public InFuncPackBase

{

InFuncPack1(FuncType pFunc,ArgType1 arg1) : m_pFunc(pFunc) , m_arg1(arg1) {}

~InFuncPack1() { DirectCall(); }

virtual void DirectCall(BOOL bClear=true)

{

if(m_pFunc)

{

m_pFunc(m_arg1);

}

if(bClear)

m_pFunc=NULL;

}

virtual void clear (){m_pFunc=NULL;}

FuncType m_pFunc;

ArgType1 m_arg1;

};

//Constructor of one parameter

template<typename FuncType,typename ArgType1>

CFuncPack(FuncType pFunc,ArgType1 arg1)

{

m_pInstance=new InFuncPack1<FuncType,ArgType1>(pFunc,arg1);

}

void clear(){m_pInstance->clear();}

InFuncPackBase* m_pInstance; //pointer to derived class

};

运用

使用以上CFuncPack模板类后,本文开始处的TextFileLines函数代码可改为如下,相对于原来代码,在代码未有明显扩充的情况下,系统资源的释放有了充分的保证。

int TextFileLines(const char* szTxtFile)

{

//declare arguments

HANDLE hFile;

HANDLE hMap;

char* lpBuf;

int cbLines;

//acquire resources

hFile=CreateFile(szTxtFile,GENERIC_READ,0,0,OPEN_EXISTING,0,0);

CFuncPack aFile(CloseHandle,hFile);

hMap=CreateFileMapping(hFile,NULL,PAGE_READONLY,0,0,NULL);

CFuncPack aMap(CloseHandle,hMap);

lpBuf=(char*)MapViewOfFile(hMap,FILE_MAP_READ,0,0,0);

CFuncPack aMapofView(UnmapViewOfFile,lpBuf);

//use resources,calculate number of lines in buffer

cbLines =CalcLines(lpBuf);

return cbLines;

}

其实还可以做的更多。看到clear函数吗?它能使函数支持“事务功能”。譬如使用WinCE的对象存储技术进行项目数据的生成函数如下:

bool CreateProject(LPCTSTR szDBFile,LPCTSTR szDesignFile)

{

//Create Database

CEGUID GuidDB;

CeMountDBVol(&GuidDB, szDBFile,CREATE_NEW);

CFuncPack aFile(DeleteFile, szDBFile);

CFuncPack aDB(CeUnmountDBVol,&GuidDB);

//Create Tables

HANDLE hTable1=CreateTable(….); //Create Table 1

CFuncPack aTable1(CloseHandle,hTable1);

HANDLE hTable2=CreateTable(….); //Create Table 2

CFuncPack aTable2(CloseHandle,hTable2);

//Import design data

bool bRes=ImportDesignFile(szDesignFile);

if(!bRes)

return FALSE;

//Reserve resource(database)

aTable2.clear();

aTable1.clear();

aDB.clear();

aFile.clear();

return true;

}

在上面的函数中,如果所有的操作都成功完成,则项目数据库和数据库表均会得到保存;否则,数据库表和数据库将会关闭,数据库文件将会删除,一切都会回到开始时的状态,不会留下和泄露任何资源,达到“事务处理”的功能。

以上代码,在Win2000+SP2、Visual C++6.0上通过编译。

胡乐秋

2003-3-27

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
>>返回首页<<
推荐阅读
 
 
频道精选
 
静静地坐在废墟上,四周的荒凉一望无际,忽然觉得,凄凉也很美
© 2005- 王朝网络 版权所有