我的意思是我的真实头文件看起来像这样:
#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();
};
标题只是声明无论如何正确吗?他们永远不会传递给编译器。我已经宣布了用户将使用的内容。
这可能吗?如果没有,为什么不呢?
答案 0 :(得分:7)
这是一个定义规则违规。你偏离一个令牌的那一刻。
a类中可以有多个定义,[...] 程序规定每个定义出现在不同的地方 翻译单元,并提供满足以下要求的定义 要求。鉴于这样一个名为D的实体在多个中定义 翻译单位,然后
- D的每个定义应由相同的令牌序列组成;和
如果您违反此类ODR,您的程序可能会轻易中断。而你的构建系统根本没有义务甚至警告你。
答案 1 :(得分:3)
您无法定义两次课程。它打破了One Definition Rule(ODT)。不幸的是,MyLibrary
做到了这一点。
它们永远不会传递给编译器
他们会的。必须在编译时知道类的成员,以便编译器可以确定类的大小。
答案 2 :(得分:1)
标题只是声明无论如何正确吗?他们永远不会传递给他们 编译器。我已经宣布了用户将使用的内容。
没有。标头是源代码的一部分,与源文件一起编译。它们包含编译器理解如何使用代码所必需的信息(在您的情况下,使用类MyLibrary
)。
例如,您希望库用户能够创建类MyLibrary
的对象,因此您可以导出构造函数。但是,这还不够:编译器需要知道要创建的对象的 size ,除非指定所有字段,否则这是不可能的。
在实践中,决定向库用户公开什么以及隐藏什么作为实现细节是一个难题,需要详细检查库使用和语义。如果你真的想隐藏类内部作为实现细节,这里有一些常见的选项:
答案 3 :(得分:0)
标题仅仅是声明,对吗?它们永远不会传递给编译器。
对文件执行#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
,它将携带析构函数的逻辑。