c++ - 使用共享库中的多态对象安全吗?

标签 c++ dynamic polymorphism shared-libraries rtti

我有一个接口(interface),我在一个共享库中有一个该接口(interface)的实现,谁知道它是用什么编译的。

dynamic_cast 仅适用于多态类型。所以我可以假设它不使用 RTTI,所以我禁用它,然后编译。

而且由于没有标准化的多态性方法,多态类型对象的布局是未知的,其 vtable 的布局也是如此。

因此出现了一个问题:我可以使用共享库中的多态类型的对象,该共享库是使用与我使用的编译器不同的编译器编译的吗?更重要的是,为什么?

最佳答案

C++ 没有标准化的 ABI,但如果您的接口(interface)遵循一些规则,您可以实现大多数编译器之间的 ABI 兼容性——尤其是在 Windows 上。这种技术在 Microsoft 的 COM ( https://en.wikipedia.org/wiki/Component_Object_Model ) 中最为著名,但我也在 Steinberg 的 VST 模型架构 ( https://steinbergmedia.github.io/vst3_doc/base/index.html ) 和“openvr”库 ( https://github.com/ValveSoftware/openvr/blob/5aa6c5f0f6520c59c4dce124541ecc62604fd7a5/headers/openvr.h#L1940 ) 中看到了它。

以下是此类 ABI 兼容 C++ 接口(interface)的基本规则:

  1. 只有纯虚拟方法。这使得多重继承相对不成问题。一个重要的警告是,只允许实现方执行从一个接口(interface)到另一个接口(interface)的 static_cast(以保证正确的 this 指针调整)。为了解决这个问题,每个COM接口(interface)都提供了QueryInterface()方法,有点类似于dynamic_cast
  2. 没有虚拟析构函数。一些编译器生成超过 1 个 vtable 条目(参见 Why do I have two destructor implementations in my assembly output? )。这意味着您必须实现自己的机制来从基指针中析构对象。 COM 和 VST3 通过 addRef()release() 使用引用计数,并且它们有自己的客户端智能指针类型。或者,您可以使用一个简单的 destroy() 方法并将您的对象实例存储在常规 std::unique_ptrstd::shared_ptr 中自定义删除器。
  3. 内存管理不得跨越接口(interface)边界,即您不得在一侧分配内存并在另一侧解除分配,因为每一侧都可能使用不同的运行时。该库必须提供一个自由函数或接口(interface)方法来释放对象。它还可能允许用户传递分配器,因此内存管理保留在客户端。
  4. 没有重载的虚拟方法。大多数编译器通常按照声明的顺序将方法放在 vtable 中,但显然 vtable 中重载方法的顺序因编译器而异。
  5. 所有方法参数必须是原始类型或具有稳定对象布局(最好是 POD)的公共(public)类。您不得使用 C++ 标准库中的任何类,例如 std::string,因为实现不稳定。
  6. 界面一旦发布,就绝不能更改。您可以通过继承现有接口(interface)来扩展现有接口(interface):
class IFoo { 
public:
    virtual void foo() = 0; 
};

class IFooEx : public IFoo {
public:
    virtual void bar() = 0; 
};

或者添加一个新的接口(interface)并使用多重继承。

话虽这么说,作为库作者,您在选择此技术之前应该三思而后行,特别是如果您计划为其他语言添加绑定(bind)。尽管这种 C++ 接口(interface)的 vtables 可以转换为函数指针的 C 结构,但通常的方法是

a) 从 C API 开始并提供客户端 C++ 包装器

b) 使用常规 C++ 接口(interface)并在其上提供 C 层。

但是通过正确的抽象,类 COM 的 C++ 接口(interface)可以很好地用于编程。

关于c++ - 使用共享库中的多态对象安全吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57999911/

相关文章:

c# - 使用唯一 ID 以编程方式创建按钮

c++ - C++ 多态性中的访问冲突(虚拟指针函数指向错误的位置?)

ruby-on-rails - 如何在 Rails 3.1 Assets 管道中动态要求 Assets ?

c++ - 构造控制台菜单类层次结构的最佳方法?

c# - Linux 下 AutoResetEvent 的 C++ 等价物是什么?

c++ - OpenCV copyTo() 掩码错误

python - 使用相对导入动态导入模块的正确方法?

c++ - 模板函数以外的 OCaml 多态性示例?

c++ - 如何在没有多态效果的情况下调用虚方法?

c++ - 我们如何测试是否可以使用纯右值调用某种类型的表达式?