我们有一个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);
};
我有几个问题。
我知道,这些问题相互交叉,但我在构建库及其在客户端代码中的进一步使用方面存在一些差距,如果您可以就此主题推荐一些书籍或文章,那就很好。< / p>
答案 0 :(得分:2)
我会在你的情况下使用Factory-Pattern(老实说,我总是使用工厂模式)。
在这种情况下,您只会导出两个函数
DLL_SPEC A *createClassA();
DLL_SPEC void destroyClassA(A *obj);
对于其余部分,您需要任何说明符virtual
,这使得思考更容易处理。
优点:
缺点:
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&#39;到实现&#39; Aimplementation&#39;它继承自界面&#39; IA&#39;将被出口。界面将与您的情况类似:
#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
,主要内容如下:
void main()
{
IA *a=IA::createIA();
a->Geta();
IA::destroyIA(a);
}
@Christophe在评论部分总结了如下:
所以诀窍是在客户端,编译器从中推断出来 header的类定义了对象的vtable布局。然后他 使用vtable间接生成调用而不需要公开 功能名称!当然还使用相同的调用约定。
通常我只为每个库导出两个函数。一个create / init和destroy / close。在给出一个类的第一个实例后,我或多或少暴露了整个接口。这使得代码在我看来更具可读性。这种模式也可以用于插件系统,其中客户端(例如Chrome)提供头部并且插件-DLL必须满足接口。