问这个问题的目的是为了加深我的理解。
我试图动态加载本机 DLL,并注意到一个我无法理解的现象。
因此,鉴于我的 DLL 具有以下过于简化的导出类。
ClassA {
SubClassA subClass;
ClassA() {
subClass = new SubClassA;
}
~ClassA() {
delete subClass;
}
}
我尝试了两种方法来链接到 DLL 并在单独的 C++ 控制台项目中调用构造函数:
方法一:静态链接(成功)
#include <ClassA.h>
void main() {
ClassA* myClass;
myClass = new ClassA();
}
方法 2:动态链接(因内存访问冲突而失败)
#include <ClassA.h>
#include <windows.h>
void main() {
HMODULE m_NativeLibraryHandle;
ClassA* myClass;
typedef ClassA* (__stdcall* ClassAConstructor)();
ClassAConstructor p_Constructor;
m_NativeLibraryHandle = ::LoadLibrary(L"ClassA.dll");
p_Constructor = (ClassAConstructor)GetProcAddress(m_NativeLibraryHandle, "decoratedNameofConstructor");
myClass = p_Constructor();
}
在 DLL 项目中调试显示,在
subClass = new SubClassA;
处引发了违规,其中 this
指针显示“无法读取”。 (我的猜测是new SubClassA
未能分配给subClass
。)
上述两种方法之间的实际差异是什么导致了行为的不同?我的理解是,
LoadLibrary
和GetProcAddress
基本上做的是Linker正在做的事情,所以理论上它们应该表现相似。
在 C++ 中调用 a 构造函数和调用 a 构造函数的函数指针的区别nce如下:
您无法在 C++ 中调用构造函数。当您使用
new
关键字、在堆栈上声明对象等时,该语言会为您执行此操作。构造 new MyClass()
可能看起来像构造函数调用,实际上它确实 包含 一个构造函数调用,因此可能会被误认为是构造函数调用,但事实并非如此;它是一个新的对象表达式,其中包括构造函数的调用。
构造函数的函数指针不存在,因为你无法获取构造函数的地址,就这么简单。
即使您可以以某种方式找出构造函数的损坏名称,并从 DLL 获取该构造函数的入口点,您也不应该尝试调用它。你不应该这样做。构造函数只能由语言调用。
这是因为构造函数假设内存已分配,并继续初始化该内存。该语言会处理这一点,但直接调用构造函数则不会。构造函数期望将未初始化的
this
传递给它,但您没有分配任何内存,也没有将任何内容传递给构造函数。结果正如预期的那样:崩溃。 (想想看:构造函数不能在内部分配内存,因为如果对象是在堆栈上初始化的,那么它会如何工作?)
您可以使用工厂方法让您的 DLL 工作。工厂方法是一个公共静态方法,它执行
new ClassA()
并返回结果。然后,从DLL中获取工厂方法的地址,并调用它。