条款14:有意识的限制智能指针的生命周期
更多条款请前往原文出处:http://blog.youkuaiyun.com/liuchang5
我们看一看下面这个例子会发生什么:
int main()
{
hrRetCode = CoInitialize(NULL);
KG_COM_ASSERT_EXIT(hrRetCode);
CComPtr<IHello> spHello = NULL;
spHello.CoCreateInstance( CLSID_HELLO );
KG_ASSERT_EXIT(spHello);
...
Exit0:
CoUninitialize();
return 0;
}//运行结束后会崩溃
上面程序崩溃了。原因是没有限制智能指针的生命周期,CoUninitialize执行之后main函数才返回,此时智能指针出栈自动调用COM接口的Release方法。于是程序崩溃了~
正确的方法是将智能指针限定到某个特殊的栈空间中去,或者提前进行=NULL操作使其释放。而我更推荐于前者,因为他能保证即便是在异常情况下你的资源也会被正确的释放:
int main()
{
hrRetCode = CoInitialize(NULL);
KG_COM_ASSERT_EXIT(hrRetCode);
{ //使用一个scope限制智能指针的声明周期
CComPtr<IHello> spHello = NULL;
spHello.CoCreateInstance( CLSID_HELLO );
KG_ASSERT_EXIT(spHello);
...
}
Exit0:
CoUninitialize();
return 0;
}
如果你觉得使用这种scope的方式不好看,那你也同样可以将所需使用的智能指针放到一个函数中去。并通过一个函数名标示清楚这段代码的大概意图。
再看一下下面这个例子,这个例子在前面的章节中出现过,但它还不理想。因为我们发现g_myStack是放在全局作用域中的。如果进行类似如下的操作则程序会崩溃。
MyStack<CComPtr<ICalculator>> g_myStack(1000); //全局作用域中的智能指针作用
//域大于CoInitialize到CoUninitialize
void func()
{
for (int i=0; i<500; i++)
{
CComPtr<ICalculator> spCalculator = NULL;
spCalculator.CoCreateInstance(CLSID_CALCULATOR);
g_myStack.push(spCalculator);
}
...
for (int i=0; i<100; i++) //完成操作后MyStack中还存在400个资源
{
CComPtr<ICalculator> spCalculator = g_myStack.pop();
spCalculator->DoSomething();
}
}
int main()
{
hrRetCode = CoInitialize(NULL);
KG_COM_ASSERT_EXIT(hrRetCode);
func();
Exit0:
CoUninitialize();
return 0;
} //别指望智能指针类会帮你处理好多余的资源,程序在这里直接崩溃了。
解决的办法根据情况而定:
1.如果你确实希望 g_myStack在全局上,那么你必须要有有效的代码保证其在程序运行完之前g_myStack为空,而不去依赖智能指针为你释放掉这部分资源。那么在CoUninitiallize执行前,准备一个特定的函数清理所使用的资源。即便没有相应的函数对资源进行处理,也至少用一个断言来尽可能早的发现这种情况。
2.否则呢?若你有办法减小g_myStack的作用域。那就尽可能的将他限制到一个栈空间上。当然这个栈空间的作用域是要小于CoInitialize与CoUninitialize间的作用域的。
这一节结束了。