问题
最近在对一份代码进行重构的时候,发现在特殊情况下,组件的销毁接口会因为程序的异常而得不到调用,这个虽然不是什么大问题(整个程序都出现异常了,进程退出是即将发生的事情),但是对于有点完美主义的我来说,这个的的确确的,降低了程序的异常安全性。
解决方法
我们先来看下面一张图,它描述了程序改造之前的交互场景:
在上图中,我们在CWinApp对象的InitInstance中调用了组件的Init接口初始化组件,然后,如果程序正常退出,CWinApp对象的ExitInstance会被调用,我们在该方法中调用组件销毁接口Exit,这样的成对调用方式,确保组件内部资源的正常初始化和释放。
但是,如果因为程序的Bug,导致正常的程序流没有走到ExitInstance,则组件的Exit接口不会得到调用,从而发生资源泄漏。
将程序进行一点改造,如下图所示:
工作原理
在上图的改造中,我们将组件的初始化和销毁接口的调用封装到一个全局对象的构造和析构方法中,实现组件在程序实例启动时的自动调用。对象的构造析构机制,也确保了组件的销毁接口会得到正常调用。
另外这样做的好处是,你不用担心程序的其他部分使用组件功能时,组件还没有初始化,因为我们的ComponentLauncher全局对象,就像CWinApp全局对象一样,它的构造函数将早于程序的main入口点,所以,我们确保了组件的初始化调用。
对于组件销毁,也是一样。程序的其他代码在接近程序退出的时间点的时候,依然可能会调用组件的业务功能,这个时候,你也无需担心组件被过早的销毁了,因为全局对象的析构位于程序的main入口点之后。
总结
1) 使用这个方法,我们需要定义一个简单的ComponentLauncher对象,其构造函数调用组件初始化接口,析构函数调用组件的销毁接口。实现自动化的成对调用。
2) 需要定义一个全局ComponentLauncher对象,就像CWinApp对象一样。
3) 这个方法的缺点:如果组件是一个大型工程,其初始化或销毁过程十分耗时,则从用户体验上来说:这个程序启动或退出比较慢。这个就需要开发者对程序健壮性和用户体验进行权衡了。
我的建议是:用户喜爱的程序,才是一个好程序。