C++ 中的纯虚函数机制如何公开来自 DLL 的函数

How does pure virtual functions mechanism in c++ exposes functions from DLL

本文关键字:函数 何公开 DLL 机制 C++      更新时间:2023-10-16

考虑下一个 c++ 代码片段

1. 在 EXE 中:

Base.hpp

#ifndef _BASE_H_
#define _BASE_H_ 
class Base
{
public:
Base(){};
virtual double Add(double &x, double &y) = 0; 
};
#endif

主.cpp

#include <iostream>
#include "Base.hpp"
#include "DerivedFactory.hpp"
void main()
{
Base *theBase = DerivedFactory::Create();
double x = 4.9, y = 3.3,z;
z = theBase->Add(x, y);//Works when Add is pure virtual function, but how??? 
//Linker error when Add is not pure virtual
}

2. 在隐式链接的 DLL 中

衍生.hpp

#ifndef _DERIVED_H_
#define _DERIVED_H_
#include "Base.hpp"
class Derived : public Base
{
public:
double Add(double &x, double &y);
};
#endif

衍生工厂.hpp

#ifndef _DERIVEDFACTORY_H_
#define _DERIVEDFACTORY_H_
#include "Derived.hpp"
class DerivedFactory
{
public:
__declspec(dllexport) static  Derived* Create();
};
#endif

派生.cpp

#include "Derived.hpp"
double Derived::Add(double &x, double &y)
{
return x + y;
}

衍生工厂.cpp

#include "DerivedFactory.hpp"
Derived* DerivedFactory::Create()
{
Derived* theDerived = new Derived;
return theDerived;
}
  1. 主要问题是,当唯一从 dll 导出的函数是 Create(( 时,exe 如何"知道"解决从 dll 添加的正确实现?

  2. 为什么当 Add(( 是"只是"虚拟而不是纯虚拟时出现链接器错误?

  1. DLL 将在其全局数据部分的某个位置包含Derived的 vtable。在 DLL 中创建的Derived实例的 vtable 指针将分配给该地址。

  2. 如果BaseAdd()声明不是纯虚拟的,则必须在声明之上为其提供定义。

编辑:有关vtable是什么的解释,请参阅此答案: 为什么需要虚拟表?

从纯粹的风格角度来看,我还要提到,在这种情况下,工厂通常会返回Base类的实例,如下所示:

衍生工厂.hpp

#include "Base.hpp"
class DerivedFactory
{
public:
__declspec(dllexport) static  Base* Create();
};

这样,Derived的封装将完全整齐地保留在 DLL 中。

派生中的虚拟表由 DLL 在创建实例时设置,因此当通过 Base 指针调用方法时,虚拟表将已经在虚拟表中设置了正确的地址(EXE 将使用来自 DLL 的派生虚拟表(。

请注意,EXE 在链接时不会从 DLL 导入虚拟表,因为它不需要(它通过 Base 接口访问 DLL 派生在内存中的虚拟表(。因此,它不仅适用于隐式链接,而且适用于在运行时加载 DLL 时(LoadLibrary/GetProcAddress - 但为此,工厂方法最好不是工厂类的一部分,而是独立的外部"C"导出函数(。

有关纯虚拟接口和 DLL 的更多详细信息,另请参阅此处:为我的 C++ 应用程序提供 SDK