是否可以在不声明其方法的情况下保留裸类定义?
Posted
技术标签:
【中文标题】是否可以在不声明其方法的情况下保留裸类定义?【英文标题】:Is it possible to keep a naked class definition without declaring it's methods? 【发布时间】:2014-01-16 13:43:22 【问题描述】:在重构我之前的问题之前,我认为这有点偏离......
标题几乎是在问我的问题。
如何在不提供方法并产生以下错误的情况下单独保留类定义?
这样做的原因是因为我想在一个单独的 DLL(其中包含方法)中创建一个对象,但只返回一个指向我的主程序的引用指针。 顺便说一下,这是显式导出。
错误 1 错误 LNK2001: 无法解析的外部符号 "public: int __thiscall ObjTest::getValue(void)" (?getValue@ObjTest@@QAEHXZ)
class ObjTest
private:
int x;
public:
int getValue();
;
【问题讨论】:
类定义必须有方法的声明。但方法定义可以放在单独的.cpp
文件中。
如果我想在一个单独的 DLL(包含方法)中创建一个对象,但只从我的主程序中引用一个指向它的指针怎么办?
你上面提到的应该可以正常工作,因为 C++ 知道指针只是一个 int。但是,如果您尝试取消引用指针并从主文件调用其函数之一(在 DLL 中实现),而不加载该 DLL,我相信它会出错。
DLL 具有 ObjTest 类的 .h 和 .cpp。它正在导出 1 个函数,该函数创建它的一个对象并返回一个指针。在我的主程序中,我需要获取该指针,而我能想到的唯一方法就是添加 DLL 的 .h 文件并使用它。但正如我所说,我得到链接器编译错误。另外,让我明确一点,这是显式导出,而不是 dll 导入。
@hyde:只要声明类的实例,将其设为纯虚拟会导致编译器错误。
【参考方案1】:
由于您需要使用 LoadLibrary() 加载 .dll,您可以公开一个纯虚拟类,并让 .dll 返回它的子类:
你把它们分成两个文件:
文件 ObjTest.h:
class ObjTest
public:
virtual int getValue() = 0;
;
ObjTest *CreateObjTest();
文件 ObjTest.cpp:
#include "ObjTest.h"
class ObjTestImpl : public ObjTest
int x;
public:
virtual int getValue();
;
int ObjTestImpl::getValue()
return x;
ObjTest *CreateObjTest()
return new ObjTestImpl();
您编译 ObjTest.cpp 并从中创建一个 .dll。您的主要可执行程序将需要 LoadLibrary() 您的 .dll,使用 GetProcAddress()
提取 CreateObjTest
作为函数指针并调用它以返回新的 ObjTest 。
(您可能还必须创建一个DeleteObjTest()
函数 - 如果您的主可执行文件和 .dll 最终使用不同的 CRT,它们将具有不同的堆,因此您需要调用 .dll 而不是仅仅执行delete myObj
.)
另一种方法是将所有内容封装在 C API 中,并通过 .dll 将不透明的句柄传递给 C 函数,而不是处理 C++ 对象。
【讨论】:
我没有隐式导入 dll,我正在导出它,因此不幸的是上述方法不起作用;/ 因为你所说的基本上是隐式导入一个dll。 dll 将包含文件的 .h 和 .cpp。主程序将包含 dll 的 .h 文件,并且链接器通过隐式指向 dll 的 .lib 文件来修复。好吧,我正在导出函数——所以没有 .lib 文件——它只是我 LoadLibrary() 并使用 GetProcAddress() 读取它的函数签名的 .dll。总而言之,添加一个裸 dll .h 函数会给我链接器错误。 @AndyCarter 非常非常重要的事实是您希望使用 LoadLibrary() 动态加载 .dll,而不是静态链接到 .dll。在这种情况下,是的,它不会起作用。 在使用 LoadLibrary 时用替代方法编辑了答案。 耶!那工作得很好。真的很感激@nos。所以我只需要手动导出类并声明它是虚拟的。【参考方案2】:您混淆了定义和实现。您已定义但未实施的方法。因此,编译器编译代码没有错误,因为方法已定义,链接器会创建错误,因为该方法没有实现(未定义符号)。
DevStudio 编译器允许您将 DLL 中的类导入应用程序:-
class __declspec (dllimport) ClassName
// members, etc
在 DLL 源中,将“dllimport”更改为“dllexport”。
更多信息请参见this MSDN article。
如果你想隐藏数据成员和私有方法,那么你需要查看pimpl idiom。
【讨论】:
"当你声明一个类 dllexport 时,它的所有成员函数和静态数据成员都被导出。你必须在同一个程序中提供所有这些成员的定义。否则会产生链接器错误。"这正是我的问题。 “如果您导出类类型的数据或返回类的函数,请务必导出该类。”好的,我将导出该类,但这将如何解决我的链接器错误? @AndyCarter:链接器错误将通过在需要它的那些翻译单元的主应用程序中包含类的头文件来解决,但将类声明为dllimport
。在 DLL 中,将类声明为 dllexport
。这意味着应用程序将知道定义并且 DLL 将包含实现。当发现对导入类的访问时,链接器将放入存根代码以加载 DLL 并解析各种方法和数据的地址。以上是关于是否可以在不声明其方法的情况下保留裸类定义?的主要内容,如果未能解决你的问题,请参考以下文章
在 Kotlin Native 中,如何在不使用 C 指针的情况下将对象保留在单独的线程中,并从任何其他线程中改变其状态?
如何在不刷新页面加载的情况下保留 jQuery 和 Bootstrap 功能?
用 memset() 写入的内存在不调用 free() 的情况下会在内存中保留多长时间?