c++中的纯虚函数机制如何从DLL中暴露函数
Posted
技术标签:
【中文标题】c++中的纯虚函数机制如何从DLL中暴露函数【英文标题】:How does pure virtual functions mechanism in c++ exposes functions from DLL 【发布时间】:2017-07-12 20:04:25 【问题描述】:考虑下一个 c++ 代码片段
1.在 EXE 中:
Base.hpp
#ifndef _BASE_H_
#define _BASE_H_
class Base
public:
Base();
virtual double Add(double &x, double &y) = 0;
;
#endif
main.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 中
Derived.hpp
#ifndef _DERIVED_H_
#define _DERIVED_H_
#include "Base.hpp"
class Derived : public Base
public:
double Add(double &x, double &y);
;
#endif
DerivedFactory.hpp
#ifndef _DERIVEDFACTORY_H_
#define _DERIVEDFACTORY_H_
#include "Derived.hpp"
class DerivedFactory
public:
__declspec(dllexport) static Derived* Create();
;
#endif
Derived.cpp
#include "Derived.hpp"
double Derived::Add(double &x, double &y)
return x + y;
DerivedFactory.cpp
#include "DerivedFactory.hpp"
Derived* DerivedFactory::Create()
Derived* theDerived = new Derived;
return theDerived;
主要问题是,当唯一导出的函数是 Create() 时,exe 如何“知道”从 dll 中正确执行 Add?
当 Add() 是“只是”虚拟而不是纯虚拟时,为什么会出现链接器错误?
【问题讨论】:
另外说明,以下划线开头后跟大写字母的名称(_BASE_H_
、_DERIVED_H_
、_DERIVED_FACTORY_H_
)和包含两个连续下划线的名称保留供执行。不要在你的代码中使用它们。
另外,将 Base 析构函数设为虚拟或受保护。使用非虚析构函数,对Base指针调用delete是UB;受保护时,会阻止调用 delete 并且您可能有一个单独的销毁方法(这更好,因为 DLL 和 EXE 可能有不同的堆)。
【参考方案1】:
DLL 将在其全局数据部分的某处包含Derived
的vtable。在 DLL 中创建的 Derived
的实例会将其 vtable 指针分配给该地址。
如果Base
的Add()
声明不是纯虚的,则必须在声明之上为其提供定义。
编辑:关于什么是 vtable 的解释,请参考这个答案: why do I need virtual table?
从纯粹的风格角度来看,我还要提一下,在这种情况下,工厂通常会返回 Base
类的实例,如下所示:
DerivedFactory.hpp
#include "Base.hpp"
class DerivedFactory
public:
__declspec(dllexport) static Base* Create();
;
这样,Derived
的封装完全且整齐地保留在 DLL 中。
【讨论】:
【参考方案2】:Derived 中的虚拟表是 DLL 在创建实例时设置的,所以当通过 Base 指针调用方法时,虚拟表中已经有正确的地址设置在虚拟表中(EXE 将使用 Derived 虚拟来自 DLL 的表)。
注意EXE在链接的时候不会从DLL中导入虚拟表,因为它不需要(它通过Base接口访问内存中DLL的Derived的虚拟表)。因此,它不仅适用于隐式链接,而且适用于在运行时加载 DLL 时(LoadLibrary/GetProcAddress - 但工厂方法最好不是工厂类的一部分,而是独立的 extern "C" 导出函数)。
有关纯虚拟接口和 DLL 的更多详细信息,另请参阅此处:Providing SDK for my C++ Application
【讨论】:
以上是关于c++中的纯虚函数机制如何从DLL中暴露函数的主要内容,如果未能解决你的问题,请参考以下文章