我有这样的功能:
bool op1();
bool op2();
bool foo() {
CoInitialize(nullptr);
if (!op1()) {
CoUninitialize();
return false;
}
// do more stuff, then call op2...
if (!op2()) {
CoUninitialize();
return false;
}
// happy path
CoUninitialize();
return true;
}
我想重构foo()
以抛出异常:
void foo() {
CoInitialize(nullptr);
if (!op1()) {
CoUninitialize(); // I'm lazy, can't I automate this call?
throw std::exception("Failed");
}
// ...
但每次发生错误时我都必须致电CoUninitialize()
。
我想过将COM初始化调用包装在一个类中,所以析构函数will do the cleanup,但是有一个未使用的对象让我觉得很奇怪:
class comlib {
public:
comlib() { CoInitialize(nullptr); }
~comlib() { CoUninitialize(); } // automated!
};
void foo() {
comlib nobodyEverCallsMe;
if (!op1()) {
throw std::exception("Failed");
}
// ...
有更好的方法吗?
答案 0 :(得分:2)
Raymond Chen已经using this method了一段时间,所以我确定没关系,只记得只有CoUninitialize
CoInitialize
才能拨打SUCCEEDED
!
class CCoInitialize {
HRESULT m_hr;
public:
CCoInitialize() : m_hr(CoInitialize(NULL)) { }
~CCoInitialize() { if (SUCCEEDED(m_hr)) CoUninitialize(); }
operator HRESULT() const { return m_hr; }
};
void Something()
{
CCoInitialize init;
...
}
如果CoInitialize失败,有些人可能想要抛出构造函数,但我觉得这是不必要的,因为其他COM调用行将失败。只有在需要从CoInitialize中捕获确切的HRESULT失败代码时才能执行此操作。
答案 1 :(得分:1)
有更好的方法吗?
没有
使用RAII(责任 1 获取是初始化)在退出时清除的习惯用法是您尝试解决的问题的标准C ++解决方案。如果是COM,我建议采用以下实现:
#include <Windows.h>
#include <comdef.h>
struct com_init
{
com_init()
{
HRESULT hr{::CoInitialize(nullptr)};
if (FAILED(hr))
{
throw _com_error{hr}; // _com_error is declared in comdef.h
}
}
com_init(com_init const&) = delete;
com_init& operator=(com_init const&) = delete;
~com_init()
{
::CoUninitialize();
}
};
在您的示例中使用了这样的内容:
void foo()
{
com_init guard{};
if (!op1())
throw std::exception{"Failed"};
// ...
使用C ++ 17,可以将对象标记为[[maybe_unused]],以防止编译器警告和通信意图。
该实现使用一个在失败时抛出异常的构造函数。有很多很好的理由这样做:
<小时/> 1 通常标记“资源”,但这并不完全符合它提供的多功能性。我更喜欢“责任”。