从静态链接的C++库中抛出异常是不安全的吗?

5
我听说在C++库中抛出异常可能存在潜在的危险,特别是对于DLL,尤其是如果调用代码和库使用不同的编译器。这是真的吗?只要我坚持使用静态库,就安全了吗?请注意,我不仅谈论库内部对异常的使用,我还想将它们抛到调用代码中深处 :)
仅作澄清:假设我有一个已编译的静态库,它定义了类Foo,如下所示:
class Foo
{
public:
    // Constructor
    Foo()
    {
        /* ... Do stuff ... */        
        if (stuffwentwrong)
            throw(123); // We throw an integer error code (to make it simple) 
    }
};

有些人会这样使用它:

try 
{
    Foo foo_object;
}
catch (int i)
{
    std::cout << "Oh bum. Code: " << i; 
}

这样做安全吗?


15年前存在一些问题,但这些问题已经得到解决(除非你正在自己动态加载/卸载DLL等花哨的操作)。如果你只是直接使用DLL,那么应该没问题。 - Martin York
4个回答

3
关于GCC,至少有一种情况下从GCC生成的共享库中捕获异常可能会出现问题,即在符号可见性默认为“hidden”时忘记从共享库导出可抛出类型。 GCC Visibility Wiki页面详细介绍了这个问题以及如何防止它发生。
我不确定Windows DLL是否存在类似的问题,但这似乎很可能。

2

尤其是如果调用代码和库使用不同的编译器编译

通常情况下,你不能混合使用不兼容ABI的不同C++编译器。例如,你不能从使用MSVC编译的库中抛出异常,并尝试使用GCC捕获它。

但除此之外,通常不会有问题。

小提示:

MSVC有几种不兼容的异常模型,请勿混用。


然而,一般情况下,有许多其他微妙之处使其变得危险。例如,如果使用不同版本的CRT编译DLL,则会出现问题(例如debug与release或multithreaded与single threaded)。此外,CRT在模块级别而不是进程级别存储某些状态(例如hModule或内存跟踪信息),因此当您跨DLL边界传输依赖于CRT的对象时,所有这些东西都可能失去同步。而且错误是在运行时发生的,可能非常微妙/令人费解。总的来说要避免这种情况。 - tenfour

2

关于DLL和异常的一个常见问题:

不要在头文件中内联实现异常类。这将导致重复的虚表和RTTI信息,导致在使用代码中无法捕获异常(由于重复,异常被认为是另一种类型)。

详情请参阅:

http://marcmutz.wordpress.com/2010/08/04/fun-with-exceptions/


0

你提供的例子应该可以正常工作,但是使用DLL时,如果你抛出了一个堆分配的异常,当DLL的使用者试图释放这个堆分配的异常时,程序会崩溃。


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