dynamic_cast 和动态库边界

5
我正在阅读《C++ GUI Programming with Qt 4》,其中我发现了以下声明:

与标准的 C++ dynamic_cast() 不同,Qt 的 qobject_cast() 能够正确地跨越动态库边界工作。

类似的声明也出现在官方的 Qt 文档中。

https://doc.qt.io/qt-5/qobject.html#qobject_cast

什么意思?在C ++中我们到底不能使用dynamic_cast?虚函数呢?在动态链接库中使用它们安全吗?

3
可能是重复的问题,与dynamic_cast返回NULL但不应该相同。 - G.M.
2
@G.M. 嗯,对于那个问题唯一的答案简单地说“基本上,RTTI在模块边界上是不可靠的”,而我正在询问这到底意味着什么(我不是一个有经验的程序员)。 - Adrian
至少,如果您能够使用仅使用C ABI的简陋界面,您的代码将更具可移植性和鲁棒性。我通常尝试避免在库边界传递C++对象和任何非C内容。异常也是如此。同样,简陋的接口减少了子系统之间的耦合。 - Erik Alapää
1个回答

8
在概念上,dynamic_cast可以跨模块边界工作。但是,一些编译器/链接器会在性能名义上偷懒。在加载共享库时提高性能的一种方法是尽可能减少向其他库公开的符号数量。这降低了加载器的工作量,因为它不必解析那么多的符号。RTTI符号是被删除的符号之一,并且默认情况下并未向其他模块公开。GCC的文档描述了此问题,以及此处的解决方法。
不幸的是,这使得处理RTTI时变得混乱。构造对象的模块和执行dynamic_cast的模块可能会持有自己的、相同的RTTI符号,这使得它们在不同的模块中执行dynamic_cast时看起来像是不同类型,导致dynamic_cast意外地返回nullptr或者对于引用抛出std::bad_cast
在链接和调用dlopen()时需要特别小心,使加载器知道在模块加载时解析模块之间的RTTI符号。
此外,从历史上看,Qt支持一些平台,这些平台要么不支持RTTI,要么RTTI会带来过多的开销。因此,发明了qobject_cast以完全消除对RTTI的依赖,同时仍提供了在多态类型之间进行转换的方法。
回答您的其他问题:
  • 您可以在任何RTTI可用的地方使用dynamic_cast,但动态链接可能会使此操作变得困难。
  • 虚函数在这些情况下工作正常,因为加载器解析函数的符号。

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