C++异常设计模式

6

我想将Win32错误(从GetLastError()返回的那些)封装在某种异常类中。然而,我不想只有一个Win32异常,我希望能够有一种专门针对常见错误(例如ERROR_ACCESS_DENIED)的特殊异常可被捕获。

例如,我会声明如下类:

class WindowsException : public std::exception
{
public:
    static WindowsException Create(DWORD lastError);
    //blah

};

class ErrorAccessDeniedException : public WindowsException
{
public:
    //blah
};

然而,我希望Win32异常负责选择正确的异常返回。也就是说,抛出异常的人应该看起来像这样:

int DangerousMethod() {
    throw WindowsAPI::WindowsException::Create(GetLastError());
}

接收器可能看起来像:

try
{
    DangerousMethod();
} catch(WindowsAPI::ErrorAccessDeniedException ex)
{
    //Code for handling ERROR_ACCESS_DENIED
} catch(WindowsAPI::WindowsException ex)
{
    //Code for handling other kinds of error cases.
}

我的问题是,如果WindowsException::Create工厂方法返回一个WindowsException,那么子类型(可能是ErrorAccessDeniedException)会被切割到基本类型。也就是说,实例不能是多态的。我不想使用new'd指针,因为这会强制异常处理程序在完成后删除它。
有没有人知道一种设计解决方案,可以优雅地解决这个问题?
Billy3

你可以创建一个宏来声明几种不同的异常类型,并根据 GetLastError() 的值创建一个抛出异常的工厂;但说实话,我只见过这个问题被如下处理:http://code.google.com/p/synergy-plus/source/browse/trunk/lib/arch/XArchWindows.cpp - Nick Bolton
那种解决方案的问题在于它不允许你创建特定的异常处理程序——对于该类型的所有异常,它只有一个异常类型。 - Billy ONeal
1
顺便提一下,你应该通过引用捕获异常,而不是像你上面的代码示例中所显示的那样通过值。 - Terry Mahaffey
2个回答

12

更改

int DangerousMethod() {
    throw WindowsAPI::WindowsException::Create(GetLastError());
}

需要翻译的内容:

To

int DangerousMethod() {
    WindowsAPI::WindowsException::Throw(GetLastError());
}

这里的意思是,不要返回异常再抛出它(这会被切片,就像你观察到的那样),而是直接让你的辅助/工厂方法抛出它。


2
当我阅读这个问题时,我正在构思一个复杂的解决方案,即框架维护每个可能异常的静态实例。只有在看到Terry的解决方案后,才会发现它是“显而易见”的。(要么我们两个都很蠢 :-)) - Andrew Shepherd
1
WindowsAPI::WindowsException::Throw() 和让该函数调用GetLastError()有什么问题? - sbi
你可以 - 我在示例代码中使用了显式的GetLastError()函数,以使我想要做的更清晰明了。 - Billy ONeal

2

一些关于异常处理的背景阅读材料:http://www.informit.com/articles/article.aspx?p=373339

关于类型切割和重新抛出的说明:

当重新抛出异常 e 时,建议仅写 throw; 而不是 throw e; ,因为前者始终保留了重新抛出对象的多态性。


2
不是说那是错误的信息,但是...那跟我的问题有什么关系呢? - Billy ONeal
提醒更多可能出现的切片问题,需要进行异常处理。 - Igor Zevaka

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接