建立dll的正确方法
Right way to build dll
我们有一个类a(如下所示)。
#ifdef DLL_ON_WORK
#define DLL_SPEC __declspec(dllexport)
#else
#define DLL_SEPC __declspec(dllimport)
class A
{
friend B;
private:
int a;
int b;
vector<int> c;
public:
DLL_SPEC int Geta();
DLL_SPEC int Getb();
DLL_SPEC vector<int> Getc();
int Calc();
private:
void Seta(int val);
void Setb(int val);
void Setc(vector<int>& val);
};
我有几个问题
- 如果类A将在客户端代码中动态和静态地创建和删除,我必须将整个类A标记为DLL_SPEC吗
- 我需要为客户端代码创建"特殊"版本的头文件吗,比如SDK,其中的所有私有和不可用的客户端代码方法和字段都将被删除?我能做到吗
- 如果头文件将在客户端代码、完整类声明中使用,或者我只能指定客户端应该使用的类接口,那么它实际上应该提供什么
我知道,这些问题相互交叉,但我在构建库及其在客户端代码中的进一步使用方面有一些知识空白,如果你能推荐一些关于这个主题的书籍或文章,那就太好了。
在您的情况下,我会使用工厂模式(老实说,我总是使用工厂模式)。
在这种情况下,您将只导出两个功能
DLL_SPEC A *createClassA();
DLL_SPEC void destroyClassA(A *obj);
对于其余部分,您需要任何说明符virtual
,这使得思考更容易处理。
优点:
- 您可以将头文件分为两部分(工厂和通常的定义)。如果类的定义对于user和dll都是相同的
- 每个方法前面都没有宏修饰符
缺点:
- 只能通过factory函数创建和销毁对象。DLL内存边界将阻止在DLL中使用new和在主代码中删除。但这并不是一个真正的问题。如果使用
std::shared_ptr
,则可以提供特殊的销毁功能
您还可以将工厂函数作为static
方法放入类中。在这种情况下,您可以声明构造函数和析构函数为private,这将阻止dll的用户在没有工厂的情况下创建A:
#ifdef DLL_ON_WORK
#define DLL_SPEC __declspec(dllexport)
#else
#define DLL_SEPC __declspec(dllimport)
class A
{
friend B;
private:
int a;
int b;
vector<int> c;
public:
virtual int Geta();
virtual int Getb();
virtual vector<int> Getc();
virtual int Calc();
static DLL_SPEC A *createClassA();
static DLL_SPEC void destroyClassA(A *obj);
private:
virtual void Seta(int val);
virtual void Setb(int val);
virtual void Setc(vector<int>& val);
};
最后但并非最不重要。使用接口模式。只导出接口,而不导出类本身。将类的名称从"A"命名为"Aimplementation",这是从将导出的接口"IA"继承的。在您的情况下,界面将是这样的:
#ifdef DLL_ON_WORK
#define DLL_SPEC __declspec(dllexport)
#else
#define DLL_SEPC __declspec(dllimport)
class IA
{
public:
virtual int Geta()=0; // with C++11 please use =nullptr instead of
virtual int Getb()=0;
virtual vector<int> Getc()=0;
virtual int Calc();
static DLL_SPEC IA *createA();
static DLL_SPEC void destroyA(IA *obj);
};
这是最巧妙的方式,也是你三个问题的答案。
更新
我之前忘记了virtual
关键字,这对于这个解决方案是必要的。关键字virtual强制编译器生成一个vtable,该表包含使用new
创建的每个对象的方法的入口点。
DLL中工厂方法的典型实现如下所示:
IA* IA::createA()
{
return new Aimplemetation;
}
void IA::destroyA(IA *obj)
{
delete static_cast<Aimplementation *>(obj);
}
要在DLL用户的代码中调用Geta
,main看起来像:
void main()
{
IA *a=IA::createIA();
a->Geta();
IA::destroyIA(a);
}
@Christophe在评论部分总结如下:
因此,诀窍在于,在客户端,编译器从header的类定义对象的vtable布局。然后他使用vtable间接寻址生成调用,而无需公开函数名称!当然也使用相同的调用约定。
通常每个库只导出两个函数。创建/初始化和销毁/关闭。在返回类的第一个实例后,我或多或少地公开了整个接口。在我看来,这也使代码更具可读性。这种模式也可以用于插件系统,其中客户端(例如Chrome)提供标头,插件DLL必须完成接口。
- 跨 DLL 边界访问虚拟方法是否安全/可能?
- C++ |DLL / EXE - 如何从导出的类调用另一个类方法?
- 创建一个C++DLL以从C#DLL调用方法
- 在 c++ 中创建 dll 并在 delphi 中调用的标准方法
- 努力使用 C# 从本机 DLL 调用该方法
- Java 调用 dll 字符串返回类型方法
- 我们如何从 C# 中的 DLL 中获取C++所有方法?
- 全局挂钩 DLL 仅在 C# 主窗口处于活动状态/前台时调用 C# 回调方法
- 显式链接 DLL 和类方法
- 为什么我不能在未链接的 DLL 上调用方法,但可以这样做?C++
- 是否有其他方法将.dll文件从一个项目复制到我的启动项目中的可执行文件旁边
- 必须使用C DLL调用C#.NET方法
- 通过重新加载DLL,避免使用DLL预加载漏洞,任何更好的方法
- Unity3D 导入 C++ DLL 用于按引用方法传递
- 为什么从 DLL 调用类方法需要虚拟说明符?
- 从C#调用C DLL方法
- Java加载DLL,该DLL从JNI中的另一个DLL导出方法
- 是否可以使用 Excel::_Application::Run 调用 C# dll 方法
- Winio64 中有哪些方法.dll以及如何使用它们
- 如何从 JAVA 程序中访问文件中定义的类.DLL方法