【问题标题】:Pure virtual destructor in interface接口中的纯虚拟析构函数
【发布时间】:2011-03-03 19:17:58
【问题描述】:

这是我的问题:我正在制作一个广泛依赖于实例对象导出的 C++ DLL。 所以我通过一些导出的工厂方法将我的实际实例作为指向接口的指针返回。

我使用的接口是纯虚拟的,以避免链接问题。所以我也需要一个纯虚拟析构函数,并且我实现了一个(用谷歌搜索它是空的)。 一切都编译得很好,除了......我看不到实际的析构函数是否被调用 - 因为当我添加了一些

std::cout  我永远看不到它。

我有一些明确的“删除 obj”(编辑:它是从 DLL 中的“FreeObject”方法调用的),这不是问题。

我错过了什么吗?还有其他方法可以通过接口删除我的对象吗?

编辑:同样,我没有内存管理不一致,它都在 DLL 中。但是没有调用正确的析构函数。

【问题讨论】:

    标签: c++ destructor


    【解决方案1】:

    这就是我解决动态加载 C++ 类的特定问题的方法 -

    对所有可插拔对象都有一个基类,即

    class object_t {
    
     public:
      virtual ~object_t();
    
     //other type utils...
    
    };
    

    对所有插件接口都有一个基础接口,即

    class object_t;
    
    class interface_t {
    
     public:
      virtual object_t* object() = 0;
    
    }
    

    object_t 将具有链接,在它自己的 dll 中定义它,您将链接到插件类。 object_t 中其他有用的钩子是复制、生成、RTTI 和其他类型的工具,例如我的基础对象有 spawn()、copy()、object_name()。

    所以所有具体类都派生自 object_t 及其各自的纯虚拟接口类型,所有发布的(可插入的)接口都派生自 interface_t。

    然后你可以加载一个插件,使用你的工厂实例化接口并因此删除 -

    delete interface->object()
    

    并且由于 object_t 具有虚拟析构函数,因此将调用正确的析构函数。

    在 linux 下删除对象的位置没有问题,如果所有插件/可执行文件都链接到同一个动态 (dll) CRT,则在窗口下很好。

    【讨论】:

    • “dll”这个词意味着 Windows,在 Linux 上他会说“so”(共享对象)。此外,所有二进制文件都链接到同一个 CRT 是一个很难长期满足的要求。想象一下,如果你想编写一个 Explorer 上下文菜单插件,而微软说“为了向后兼容我们仍在使用 Visual C++ 4.0 编译的旧插件,你也必须这样做,所以忘记使用异常、模板或任何其他现代的插件中的 C++ 功能”。
    【解决方案2】:

    界面中根本不需要析构函数。只有delete 调用者才能看到正确的析构函数。在 DLL 之外,使用接口指针作为句柄,而不是您可以创建/销毁的东西。对象构造/销毁应该在 DLL 中。您可以使用引用计数或任何其他您想要的技术,只需提供适当的导出来访问和操作对象,您就可以决定封装多少内存管理。

    【讨论】:

      【解决方案3】:

      您不应跨 DLL 边界混合和匹配对 newdelete 的调用。

      相反,我推荐 COM 使用的久经考验的方法:AddRefRelease。当引用计数为零时,Release 从 DLL 内部调用 delete this;,确保匹配 newdelete

      【讨论】:

      • 已经处理好了。我已经编辑了我的问题以明确。
      • 我仍然推荐使用 COM AddRef/Release 模式。 FreeObject 听起来像是一个非常通用的名称,参数是如何定义的?删除的指针必须具有至少与声明虚拟析构函数的基类一样具体的编译时类型,以便虚拟删除发生 - delete (void*)p 永远不会在 p 上调用任何析构函数。
      • 在 10 年前,当运行时仅作为静态库提供时,跨 dll bun 干燥使用 new/delete 是一个问题。通过引入运行时 dll 的共享库版本(这意味着整个应用程序只有一个堆),这个问题得到了解决。除非您“手动”加载/卸载 dll,否则跨 dll 包使用 new/delete 不再是问题。
      • Ben Voigt,多亏了你,我终于明白我的问题出在哪里以及如何解决了。这真的是“非常通用”的方法,这阻止了它完成它的工作。我太傻了(我已经使用了 COM 模式,它工作得很好。谢谢。
      • @ALOR:很高兴为您提供帮助。 @Martin:您对“边界”这个词有一些敌意,或者只是空间不足?无论如何,COM 模式允许调用者和实现者使用不同的编译器,甚至是不同的语言。共享运行时 DLL 只会导致极端脆弱性,因为在所有部分都移植到新的编译器版本之前,任何部分都不能移植到新的编译器版本。这种耦合是优秀的软件工程师努力避免的。
      猜你喜欢
      • 2016-10-13
      • 2016-01-22
      • 1970-01-01
      • 1970-01-01
      • 2011-03-21
      • 2019-04-20
      • 2012-11-23
      • 2010-10-12
      相关资源
      最近更新 更多