在导出的类中使用std::unique_ptr

Using std::unique_ptr in an exported class

本文关键字:std unique ptr      更新时间:2023-10-16

我在DLL中有一个类,看起来像这样:

#ifdef LIB_EXPORT
#define LIB_API __declspec(dllexport)
#else
#define LIB_API __declspec(dllimport)
#endif
...
class LIB_API MyClass {
public:
  // ...public interface...
private:
 // ...some private fields...
 std::unique_ptr<OtherClass> otherPtr_;
};

现在,我认为这可能是一个问题:如果客户端代码使用稍微不同版本的unique_ptr,那么MyClass对象的内存布局实际上会与DLL中的代码所期望的有所不同。

我真的不想使用Pimpl习惯用法来从公共头中隐藏unique_ptr。我可能会推出自己的简化版unique_ptr(我只需要它功能的一个子集,例如,我不需要自定义删除程序)。但是,在我尝试之前,有其他方法可以解决这个问题吗?

您推测的问题是非常真实的,它不仅适用于标准库类的布局,也适用于您自己的类。除非您的类符合标准布局规则,否则即使给定完全相同的源代码,不同的编译器也不应该使用相同的内存布局。答案是根本不应该导出C++类。


案例#1:如果您想要unique_ptr来管理DLL的公共对象的生存期:

从DLL导出一个工厂函数和删除函数,并在公共头中放入一个包装类。包装器完全存在于客户端中,因此仅使用客户端版本的unique_ptr

包装类上未使用__declspec(dllexport)


案例#2:如果DLL内部使用unique_ptr

你应该使用继承而不是皮条客。公共头文件包含一个基类,该基类具有受保护的构造函数、纯虚拟成员函数,并且根本没有数据成员。同样,不使用__declspec(dllexport)dllexport工厂函数用于创建新实例。在DLL中,您从该接口类型继承,派生类将添加所有数据成员和函数体。客户端永远看不到任何数据成员,因此您可以自由使用C++对象,并且使用的布局是DLL的本地布局。


这两者的副作用是,琐碎的成员函数不会内联,这可能会对性能产生负面影响。但是,为每个成员访问调用DLL是实现解耦的唯一方法。