【问题标题】:Throwing C++ exceptions across DLL boundaries跨 DLL 边界抛出 C++ 异常
【发布时间】:2011-07-03 17:42:23
【问题描述】:

我已经阅读了各种关于不应如何在一个 DLL 中分配堆内存并从该 DLL 外部释放它的内容。但是抛出一个只是临时的异常对象(就像大多数异常对象一样)呢?例如:

throw my_exception( args ); // temporary: no heap allocation

当异常对象在 DLL 之外被捕获时,该对象的析构函数最终将被执行,并且该对象的非堆内存将被回收。因为它不是堆内存,所以可以吗?

【问题讨论】:

  • 你抛出的异常不是你捕获的异常;沿途制作了一份副本。也就是说,我不知道副本在哪里,也不知道它是如何被破坏的。
  • @Mark:当异常被引用捕获时,这仍然是真的吗?你有消息来源吗?
  • @Mark:AFAIK,C++ 要求异常具有可公开访问的复制构造函数,但是否实际使用它来制作副本取决于实现。它很可能是被捕获的同一物体。
  • @Ben:来自 N3225,15.1 Throwing an exception3: A throw-expression initializes a temporary object...5: The memory for the exception object is allocated in an unspecified way...@Paul:5: When the thrown object is a class object, the copy/move constructor and the destructor shall be accessible, even if the copy/move operation is elided
  • @Ben:来自 N3225,15.3 Handling an exception16: The object declared in an exception-declaration or, if the exception-declaration does not specify a name, a temporary (12.2) is copy-initialized (8.5) from the exception object...17: ...When the handler declares a reference to a non-constant object, any changes to the referenced object are changes to the temporary object initialized when the throw-expression was executed and will have effect should that object be rethrown.

标签: c++ dll exception-handling


【解决方案1】:

只有当所有模块都使用相同的 C++ 运行时,才可能跨 DLL 边界抛出 C++ 异常,在这种情况下,它们也共享一个堆。但这可能是一个维护负担,尤其是在涉及多个供应商的库时,因此不鼓励这样做。

如果您想要跨多个编译器/编译器版本/编译器设置可移植的错误处理,请使用返回码或操作系统提供的异常(例如 Windows 上的 SEH)/

【讨论】:

  • 您写道,“......在这种情况下,它们也共享一个堆。”我特意询问了不涉及堆内存的情况。
  • @Paul:我的意思是,如果你有理由(而且有很多好的理由)不跨 DLL 共享堆内存,同样的理由会阻止你共享跨 DLL 的异常。
  • 那么跨 DLL 共享一个简单的 POD 结构也不行吗?如果 DLL 中的函数按值返回结构怎么办?那个临时的最终会被摧毁。同样的问题?如果没有,为什么不呢?
  • @Paul:问题不在于异常结构,而是编译器用于查找匹配的 catch 块并执行堆栈展开的内部数据,所有这些都是高度特定于编译器的,并且可能使用全局变量,甚至可以使用堆。如果所有模块都没有共享一个运行时,你就有问题了。
【解决方案2】:

这取决于内存是如何分配的,以及分配的机制(“运行时”或“内存管理器”)是否在特定 DLL 和应用程序的其他部分之间共享。例如。 throw new my_exception( args ); 也可以根据详细信息进行排序。

您可以对异常引用进行计数,以便它具有如何销毁自己的实例(和拥有的内存)的内在知识。

使用IMalloc(参见MSDN)进行实例分配和放置new将是另一种方式(之前调用OleInitialize)...

确实,内存分配是一个问题,具体取决于所使用的内容。例如,在应用程序的不同部分混合静态链接 CRT 和动态链接 CRT 会导致问题,就像混合调试和发布代码一样。这里的问题是应该释放内存的代码使用不同的“内存管理器”。但是,如果抛出的对象知道自己的销毁,那应该没问题,因为 dtor 代码将驻留在与分配它的编译单元相同的编译单元中。

【讨论】:

  • 为什么需要 IMalloc?如果按照您的建议通过 new 抛出动态分配的、引用计数的异常对象,那么只要在 DLL 代码中完成了新/删除对,那为什么还不足以毫无问题地工作?
  • @Paul J. Lucas:这就是为什么它在我提到 IMalloc 的句子中说“将是另一种方式”。抱歉,如果不清楚。英语不是我的母语。我会把它移到一个单独的段落...
猜你喜欢
  • 2015-08-25
  • 2012-03-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-12-12
  • 1970-01-01
  • 2012-06-02
相关资源
最近更新 更多