RError.com

RError.com Logo RError.com Logo

RError.com Navigation

  • 主页

Mobile menu

Close
  • 主页
  • 系统&网络
    • 热门问题
    • 最新问题
    • 标签
  • Ubuntu
    • 热门问题
    • 最新问题
    • 标签
  • 帮助
主页 / 问题 / 978340
Accepted
Alex Nem
Alex Nem
Asked:2020-05-06 02:11:36 +0000 UTC2020-05-06 02:11:36 +0000 UTC 2020-05-06 02:11:36 +0000 UTC

从 DLL 到 C++ 的独立于编译器的类导出

  • 772

我决定创建一个带有 OpenGL 渲染器的 DLL 库,以便以后可以在任何地方(嗯,几乎)使用它。也就是不依赖于编译器和CRT的版本,把.lib和.h文件连接到你的项目就足够了,不管版本和配置都可以使用。听说用通常的方式导出类是不行的,推荐使用C接口,只导出函数。但是由于我的库是由 OOP 构想的,我仍然想找到一种方法来以某种方式导出这些相同的类。结果,我遇到了所谓的“工厂模式”,即导出创建对象并返回指向它的指针的方法。问题似乎解决了,但出现了一些问题。在描述这种方法的文章中(它在这里),建议执行以下操作:

  • 创建具有完全虚拟方法的基类(A 类)(以及清理内存的销毁方法)
  • 从上述基类(A)创建一个后代类(B类),其中所有虚拟方法都将被覆盖
  • 创建从DLL导出的对象创建方法,其中,在动态内存中(使用new)创建一个对象,但返回一个指向基类对象的指针(据我了解,指针是强制转换的)

结果,在我的代码中,它看起来像这样:

基类(接口):

#pragma once

#ifdef RENDERERGL_EXPORTS
#define RENDERERGL_API __declspec(dllexport)
#else
#define RENDERERGL_API __declspec(dllimport)
#endif

/**
 * \brief Интерфейсный класс для рендерера
 */
class RendererGLInterface
{
public:
    virtual void destroy() = 0;
    virtual ~RendererGLInterface(){};
};

后代类(主类,.h 文件)

#pragma once
#include "RendererInterface.h"

/**
 * \brief Основной класс рендерера
 */
class RendererGL : RendererGLInterface
{
private:
public:
    /**
     * \brief Очистка памяти (вызов деструктора)
     */
    void destroy() override;

    /**
     * \brief Деструктор
     */
    ~RendererGL();
};

/**
 * \brief Экспортируемая функция создания рендерера
 * \return Указатель на объект рендерера
 */
extern "C" RENDERERGL_API RendererGLInterface* __cdecl CreateRenderer();

后代类,实现(.cpp 文件)

#include "Include/RendererGL.h"

void RendererGL::destroy()
{
    delete this;
}

RendererGL::~RendererGL()
{
}

RendererGLInterface* CreateRenderer()
{
    return reinterpret_cast<RendererGLInterface*>(new RendererGL());
}

一切似乎都很好,但是..

  • 单独的接口类(带有虚拟方法)有什么意义。为什么不能返回不是基类型的指针,而是类本身类型的指针?
  • 我正确理解需要销毁方法,因为必须从 DLL 中删除类对象?但是通过删除自己有多好delete this?创建一个像“freeA”这样会删除A类对象的导出方法会更正确吗?
  • 也许目前已经有一些更方便的导出方式?来自 STL 的标准智能指针能否以某种方式让生活更轻松(如果可以,如何)?

提前致谢。

c++
  • 1 1 个回答
  • 10 Views

1 个回答

  • Voted
  1. Best Answer
    Andrey Sv
    2020-05-07T14:01:32Z2020-05-07T14:01:32Z

    这种方法直接依赖于虚函数表,只有不同的编译器以相同的方式构建这样的表,我们才能谈论使用另一个编译器编译的可执行文件中已经编译好的动态库。当然,条件是在同一平台上,位深度等。

    一般来说,使用抽象基类,称为接口,从它继承的类,称为实现。在这种情况下,实现只存在于 DLL 中,并且接口在调用程序中使用。工作顺序大致如下:

    1. 调用 DLL 函数在堆上创建一个对象并将指向该对象的指针返回给程序。
    2. 该程序使用一个抽象类(接口)来处理接收到的指针。

    最终,由于实现类是从抽象继承而来的,所以会使用多态性,编译器会在通过基抽象类访问时从虚函数表中替换值。

    接口不应包含除空方法以外的任何方法;将其设为结构而不是类更方便。任何需要隐藏的东西都可以隐藏在实现中。

    struct RendererGLInterface
    {
        virtual void destroy() = 0;
    };
    

    您导出的函数返回一个指向该类的指针:

     RendererGLInterface* __cdecl CreateRenderer();
    

    C 函数只能返回指向 struct 或 的指针void *,不能返回类。通用选项:

     void* __cdecl CreateRenderer();
    

    好吧,回答你的问题:

    单独的接口类(带有虚拟方法)有什么意义。为什么不能返回不是基类型的指针,而是类本身类型的指针?

    因为在链接时将没有实现的方法,并且您要在其中使用 DLL 中的对象的程序根本不会被组装。

    我正确理解需要销毁方法,因为必须从 DLL 中删除类对象?

    是的。就是为了这个。

    但是用 delete this 来删除自己有多好?

    如果您希望您的对象自行清理,这没有什么可谴责的。

    创建一个像“freeA”这样会删除A类对象的导出方法会更正确吗?

    它只是有所不同,但实际上,它向 DLL 添加了一个函数。类中方法的存在使我们免于这种情况。此外,在 C 函数中,您必须传递一个空指针并在内部对其进行标识才能正确删除它,这与destroy()对象知道关于自身的一切的方法不同。好吧,如果将来CreateRenderer它会创建不同的对象,那么通过他们的方法进行删除会更容易。

    也许目前已经有一些更方便的导出方式?

    总的来说,这远不是一种处理 DLL 的新方法;它长期以来一直被 Microsoft 用于COM 对象,甚至已经过时。可以将类本身作为一个整体和单独的方法导出,通常我会推荐使用。

    • 1

相关问题

Sidebar

Stats

  • 问题 10021
  • Answers 30001
  • 最佳答案 8000
  • 用户 6900
  • 常问
  • 回答
  • Marko Smith

    根据浏览器窗口的大小调整背景图案的大小

    • 2 个回答
  • Marko Smith

    理解for循环的执行逻辑

    • 1 个回答
  • Marko Smith

    复制动态数组时出错(C++)

    • 1 个回答
  • Marko Smith

    Or and If,elif,else 构造[重复]

    • 1 个回答
  • Marko Smith

    如何构建支持 x64 的 APK

    • 1 个回答
  • Marko Smith

    如何使按钮的输入宽度?

    • 2 个回答
  • Marko Smith

    如何显示对象变量的名称?

    • 3 个回答
  • Marko Smith

    如何循环一个函数?

    • 1 个回答
  • Marko Smith

    LOWORD 宏有什么作用?

    • 2 个回答
  • Marko Smith

    从字符串的开头删除直到并包括一个字符

    • 2 个回答
  • Martin Hope
    Alexandr_TT 2020年新年大赛! 2020-12-20 18:20:21 +0000 UTC
  • Martin Hope
    Alexandr_TT 圣诞树动画 2020-12-23 00:38:08 +0000 UTC
  • Martin Hope
    Air 究竟是什么标识了网站访问者? 2020-11-03 15:49:20 +0000 UTC
  • Martin Hope
    Qwertiy 号码显示 9223372036854775807 2020-07-11 18:16:49 +0000 UTC
  • Martin Hope
    user216109 如何为黑客设下陷阱,或充分击退攻击? 2020-05-10 02:22:52 +0000 UTC
  • Martin Hope
    Qwertiy 并变成3个无穷大 2020-11-06 07:15:57 +0000 UTC
  • Martin Hope
    koks_rs 什么是样板代码? 2020-10-27 15:43:19 +0000 UTC
  • Martin Hope
    Sirop4ik 向 git 提交发布的正确方法是什么? 2020-10-05 00:02:00 +0000 UTC
  • Martin Hope
    faoxis 为什么在这么多示例中函数都称为 foo? 2020-08-15 04:42:49 +0000 UTC
  • Martin Hope
    Pavel Mayorov 如何从事件或回调函数中返回值?或者至少等他们完成。 2020-08-11 16:49:28 +0000 UTC

热门标签

javascript python java php c# c++ html android jquery mysql

Explore

  • 主页
  • 问题
    • 热门问题
    • 最新问题
  • 标签
  • 帮助

Footer

RError.com

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

帮助

© 2023 RError.com All Rights Reserve   沪ICP备12040472号-5