【发布时间】:2018-03-06 22:09:00
【问题描述】:
我正在考虑在我正在编写的一些库中使用异常处理。 代码库最终可能在多个 dll 中实现。 我知道跨 dll 使用异常处理是一个坏主意 - 但是,执行以下操作是否安全?
class IDllSafeException
{
public:
virtual void AddRef() = 0;
virtual void Release() = 0;
virtual int GetErrorCode() const = 0;
virtual const char * What() const = 0;
};
假设这是一种有效的方法,我真的很想按值而不是指针来捕获。这样我就可以有一个自动调用 Release() 的包装类。
这样……
template <typename T>
class SafeExceptionT
{
public:
SafeExceptionT(T *);
SafeExceptionT(SafeExceptionT &);
SafeExceptionT & operator=(SafeExceptionT &);
~SafeExceptionT();
int GetErrorCode() const;
const char * What() const;
private:
T * m_pException;
};
typedef SafeExceptionT<IDllSafeException> SafeException;
作为一个模板意味着所有的 catch 块都将生成它们自己的布局并在析构函数中调用 can m_pException->Release() (在复制发生时使用 AddRef() )。我也可以根据需要实现不同类型的异常。
我的 catch 块将如下所示...
try
{
ThrowSomething();
}
catch(SafeException except)
{
const char * psz = except.What();
int nErrorCode = except.GetErrorCode();
}
...
void ThrowSomething()
{
// Concrete implementation omitted for brevity
throw new DllSafeException(1, "something went wrong");
}
我原以为这会起作用,但我的异常处理程序没有被捕获,即使我有一个 SafeException 的非显式构造函数。
但是我似乎只能通过指针捕捉...
try
{
ThrowSomething();
}
catch(SafeException except)
{
/// IGNORED!
...
}
catch(IDllSafeException * pExcept)
{
// CAUGHT BY POINTER but we now have to micro-manage.
const char * psz = pExcept->What();
int nErrorCode = pExcept->GetErrorCode();
pExcept->Release();
}
对于异常处理程序中的类型匹配方式是否有特殊规则?
【问题讨论】:
-
跨 dll 使用异常处理并不是一个坏主意。它只需要非常小心的处理,就像一般的 C++ 异常一样。如果在这个 dll 中定义了要抛出的异常,那么你需要从这个 dll 中为这个类导入 rtti。如果它是在其他地方定义的,那么您需要从那里为异常类导入 rtti。此外,您应该按值抛出并通过 const 引用捕获。如果您仍然想抛出一个指针,那么制作虚拟析构函数而不是 COM 样式的
Release并重新发明智能指针。 -
您不能在 DLL 边界上抛出异常,尤其是基于类的异常,除非 DLL 和调用者都是针对同一个运行时库构建的,共享同一个 RTTI,同一个内存管理器等。只是不要这样做,这是不安全的。
-
感谢 cmets,尽管似乎存在分歧。如果使用虚拟方法并通过指针抛出,我会认为内存管理问题会得到解决。此外,任何想法为什么异常没有被值作为 SafeException 捕获?如果这会成为问题,我可能会重新使用返回码。
-
如果它没有被捕获,很可能你没有从 dll 导入异常类。上面列出的条件(“DLL 和调用者都是针对同一个运行时库构建的,共享同一个 RTTI,同一个内存管理器”)实际上是安全使用从一般 dll 导入的 C++ 代码的条件,而不仅仅是异常.
-
如果我将析构函数设为虚拟,这不会导致错误 dll 中的堆释放吗?这就是我选择参考计数的原因。不过,如果我错了,很高兴得到纠正。