![](/img/trans.png)
[英]Why am I getting an “use of undefined type” error in my code, when I have the header for it included?
[英]Can I use a slim version of my header to be included with the library?
我的意思是我真正的头文件看起来像这样:
#include "some_internal_class.h"
class MyLibrary {
Type private_member;
void private_function();
public:
MyLibrary();
void function_to_be_called_by_library_users();
};
现在我想生成一个包含所有必要定义的动态库。 并且我想随附一个标题,而不是发送我在库中的每个标题。
所以我想我可以像这样创建我的标题的精简版本:
class MyLibrary {
public:
MyLibrary();
void function_to_be_called_by_library_users();
};
无论如何,标题只是声明,对吗? 它们永远不会传递给编译器。 我已经声明了用户将使用什么。
那可能吗? 如果没有,为什么不呢?
这是一个定义规则的违反。 你偏离一个令牌的那一刻。
一个类类型可以有多个定义,[...] 在一个程序中,只要每个定义出现在不同的翻译单元中,并且这些定义满足以下要求。 给定在多个翻译单元中定义的名为 D 的实体,则
- D 的每个定义都应由相同的记号序列组成; 和
如果您像这样违反 ODR,您的程序可能很容易中断。 而且您的构建系统根本没有义务警告您。
标题只是声明,对吗? 它们永远不会传递给编译器。 我已经声明了用户将使用什么。
不可以。头文件是源代码的一部分,与源文件一起编译。 它们包含编译器了解如何使用代码所需的信息(在您的情况下,使用类MyLibrary
)。
例如,您希望库用户能够创建MyLibrary
类的对象,因此您导出构造函数。 但是,这还不够:编译器需要知道要创建的对象的大小,这是不可能的,除非您指定所有字段。
在实践中,决定向库用户公开什么以及隐藏什么作为实现细节是一个难题,这需要对库的使用和语义进行详细检查。 如果你真的想隐藏类内部作为实现细节,这里有一些常见的选项:
无论如何,标题只是声明,对吗? 它们永远不会传递给编译器。
当您对文件执行#include
,其内容将完全按原样复制并粘贴到源文件中。 因此,即使您不直接将它们作为编译器参数传递,它们仍然是您代码的一部分,并且其中的代码将被编译到您的翻译单元中。
@lisyarus 的解决方案非常好。
但另一种选择是用 C 的方式来做。 在我看来,这是最优雅的。
在 C 中,你给你的用户一个句柄,它很可能是一个指针。
你的标题看起来像这样:
struct MyLibrary;
MyLibrary*
my_library_init();
void
my_library_destroy(MyLibrary*);
void
my_library_function_to_be_called_by_library_users(MyLibrary*);
一个非常小而简单的界面,不会向您的用户显示您不希望他们看到的任何内容。
另一个好处是,您的构建系统不必仅仅因为您向MyLibrary
结构中添加了一个字段而重新编译整个程序。
不过你必须小心,因为现在你必须调用my_library_destroy
,它会携带你的析构函数的逻辑。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.