在dll-interfaces中使用shared_ptr

Ale*_*tov 23 c++ dll boost abstract-class shared-ptr

我的dll中有一个抽象类.

class IBase {
  protected:
       virtual ~IBase() = 0;
  public:
       virtual void f() = 0;
};
Run Code Online (Sandbox Code Playgroud)

我想进入IBase加载DLL的exe文件.第一种方法是创建以下功能

IBase * CreateInterface();
Run Code Online (Sandbox Code Playgroud)

并添加虚拟函数Release()IBase.

第二种方法是创建另一个功能

boost::shared_ptr<IBase> CreateInterface();
Run Code Online (Sandbox Code Playgroud)

并且不需要任何Release()功能.

问题.

1)在第二种情况下,在dll(而不是在exe文件中)中调用析构函数和内存释放是否正确?

2)如果使用不同的编译器(或不同的设置)编译exe-file和dll ,第二种情况是否能正常工作.

phl*_*psy 19

回答第一个问题:调用dll中的虚拟析构函数 - 有关其位置的信息嵌入在对象中(在vtable中).在内存释放的情况下,它取决于您的用户的纪律IBase.如果他们知道他们必须打电话Release()并认为该例外可以在令人惊讶的方向上绕过控制流程,那么将使用正确的方法.

但是如果CreateInterface()返回shared_ptr<IBase>它可以将正确的解除分配函数绑定到这个智能指针.您的图书馆可能如下所示:

Destroy(IBase* p) {
    ... // whatever is needed to delete your object in the right way    
}

boost::shared_ptr<IBase> CreateInterface() {
    IBase *p = new MyConcreteBase(...);
    ...
    return shared_ptr<IBase>(p, Destroy); // bind Destroy() to the shared_ptr
}                                         // which is called instead of a plain
                                          // delete
Run Code Online (Sandbox Code Playgroud)

因此,您的DLL的每个用户都可以轻松防止资源泄漏.他们从来不必费心去打电话Release()或注意异常绕过令人惊讶的控制流程.

回答你的第二个问题:其他答案清楚地说明了这种方法的缺点:你的观众必须使用与你相同的编译器,链接器,设置和库.如果它们可能相当多,这可能是您图书馆的主要缺点.您必须选择:安全与更大的受众

但是存在一个可能的漏洞:shared_ptr<IBase>在您的应用程序中使用,即

{
    shared_ptr<IBase> p(CreateInterface(), DestroyFromLibrary);
    ...
    func();
    ...
}
Run Code Online (Sandbox Code Playgroud)

因此,没有实现特定对象跨DLL边界传递.不过你的指针被安全地隐藏在背后的shared_ptr,是谁打电话DestroyFromLibrary在合适的时间,即使func()的抛出异常与否.


Mar*_*n B 8

我建议不要shared_ptr在界面中使用.即使在DLL的接口中使用C++(而不是"extern C"只有例程)也是有问题的,因为名称修改会阻止您使用不同编译器的DLL.使用shared_ptr特别成问题,因为正如您已经确定的那样,不能保证DLL的客户端将使用与shared_ptr调用者相同的实现.(这是因为它shared_ptr是一个模板类,实现完全包含在头文件中.)

回答您的具体问题:

  1. 我不太确定你在这里问的是什么......我假设你的DLL将包含派生自的类的实现IBase.在两种情况下,它们的析构函数代码(以及代码的其余部分)都将包含在DLL中.但是,如果客户端启动对象的销毁(通过delete在第一种情况下调用或shared_ptr在第二种情况下让最后一个实例超出范围),则将客户端代码调用析构函数.

  2. 名称修改通常会阻止您的DLL与其他编译器一起使用...但是shared_ptr即使在同一编译器的新版本中,实现也可能会发生变化,这可能会让您遇到麻烦.我会回避使用第二种选择.

  • 您可以指定自定义删除器以供shared_ptr使用,这不是问题.问题是"标准到期"(TR1)没有规定如何实现shared_ptr,即它的内存布局应该是什么以及它应该存储引用计数的位置.DLL和客户端可以使用两个编译器进行编译,这两个编译器完全符合标准,但不同意shared_ptr在内部的内容.*这就是问题.所以不要在DLL接口中使用shared_ptr ......它可能会回来咬你. (2认同)