1

我正在重构一个 5.5k 行的 C++ DLL,将其拆分为多个较小的 DLL。不幸的是,很多代码是捆绑在一起的,在拆分 GiantDLL 的过程中,我引入了几个循环引用。

进一步来说,

//In emergeOSLib.h:
DLL_EXPORT std::wstring ELGetProcessIDApp(DWORD processID, bool fullName);

//In a couple of functions in emergeOSLib.cpp:
ELMessageBox(GetDesktopWindow(), messageText, (WCHAR*)TEXT("Emerge Desktop"),                       ELMB_OK|ELMB_ICONERROR|ELMB_MODAL);

//In emergeUtilityLib.h:
DLL_EXPORT int ELMessageBox(HWND hwnd, std::wstring messageText, std::wstring messageTitle, DWORD msgFlags);

//In a function in emergeUtilityLib.cpp:
out << ELGetProcessIDApp(GetCurrentProcessId(), false) << TEXT(": ") << debugText << std::endl;

瞧,一个循环引用。我很确定还有更多,这只是我现在正在处理的一个。

我做了一些研究,发现前向声明似乎是要走的路:
Resolve header include circular dependencies
Circular Dependency in C++

第二个链接甚至表明前向声明优于s#include .

在这一点上我有两个问题。首先,如何前向声明另一个 DLL 中的函数?我的理解是编译器仍然无法找到前向声明的函数(因为它没有将第二个 DLL 编译为第一个 DLL 的一部分)并且会抱怨。其次,通常认为哪个是更好的做法,#include声明或前向声明?

4

2 回答 2

1

前向声明与#include仅有助于声明循环性。但是你有定义循环。如果您将所有定义链接在一起,多通道链接器将解决此问题......但您不是。

基本上有两种方法可以做到这一点。首先,您可以更改ELGetProcessIDApp为实用程序库中的函数指针。最初它指向实用程序库中的一些存根值,当操作系统支持库加载时,它会用特定于操作系统的实现覆盖该函数指针。

或者,您可以使用链接器定义文件在 DLL 实际存在之前构建导入库。这将通过创建一组循环依赖的 DLL 来解决您的链接器问题。它可以工作,但它也可能导致令人惊讶的加载顺序效果并展示全局初始化排序失败。

或者,只使用单个 DLL,并将重构限制为拆分源文件。

于 2013-10-31T06:04:56.073 回答
0

如何前向声明另一个 DLL 中的函数?

你不这样做,它不起作用。您可以声明它,但链接阶段将失败。

相反,让 DLL A 正常链接到 DLL B,并且 DLL B 在运行时从 DLL A 接收函数指针(或具有虚拟函数的对象)。

于 2013-10-31T06:04:37.087 回答