考虑下一个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;
}
主要的问题是当exe中唯一导出的函数是Create()时,exe如何“知道”从dll中正确实现Add的实现?
当Add()“只是”虚拟而不是纯虚拟时,为什么会出现链接器错误?
答案 0 :(得分:3)
DLL将在其全局数据部分的某处包含Derived
的vtable。在DLL中创建的Derived
实例将其vtable指针分配给该地址。
如果Base
的{{1}}声明不是纯虚拟的,您必须在声明之上提供它的定义。
修改:有关vtable的说明,请参阅以下答案: why do I need virtual table?
从纯粹的风格角度来看,我还要提到在这种情况下,工厂通常会返回Add()
类的实例,如下所示:
Base
这样,#include "Base.hpp"
class DerivedFactory
{
public:
__declspec(dllexport) static Base* Create();
};
的封装完全,整齐地保留在DLL中。
答案 1 :(得分:2)
Derived中的虚拟表是在创建实例时由DLL设置的,因此当通过Base指针调用的方法时,虚拟表已经在虚拟表中设置了正确的地址(EXE将使用Derived虚拟表)表来自DLL)。
请注意,EXE在链接时不会从DLL导入虚拟表,因为它不需要(它通过Base接口访问DLL在内存中的Derived的虚拟表)。因此,它不仅适用于隐式链接,而且还适用于在运行时加载DLL(LoadLibrary / GetProcAddress - 但为此工厂方法最好不是工厂类的一部分,而是独立的extern“C”导出函数)。
有关纯虚拟接口和DLL的更多详细信息,请参见此处:Providing SDK for my C++ Application