繁体   English   中英

我可以使用我的标头的精简版本来包含在库中吗?

[英]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();
};

无论如何,标题只是声明,对吗? 它们永远不会传递给编译器。 我已经声明了用户将使用什么。

那可能吗? 如果没有,为什么不呢?

这是一个定义规则的违反。 你偏离一个令牌的那一刻。

[basic.def.odr]/6

一个类类型可以有多个定义,[...] 在一个程序中,只要每个定义出现在不同的翻译单元中,并且这些定义满足以下要求。 给定在多个翻译单元中定义的名为 D 的实体,则

  • D 的每个定义都应由相同的记号序列组成;

如果您像这样违反 ODR,您的程序可能很容易中断。 而且您的构建系统根本没有义务警告您。

您不能两次定义一个类。 它打破了单一定义规则(ODT)。 不幸的是, MyLibrary这样做的。

它们永远不会传递给编译器

他们会的。 在编译时必须知道类的成员,以便编译器可以确定类的大小。

标题只是声明,对吗? 它们永远不会传递给编译器。 我已经声明了用户将使用什么。

不可以。头文件是源代码的一部分,与源文件一起编译。 它们包含编译器了解如何使用代码所需的信息(在您的情况下,使用类MyLibrary )。

例如,您希望库用户能够创建MyLibrary类的对象,因此您导出构造函数。 但是,这还不够:编译器需要知道要创建的对象的大小,这是不可能的,除非您指定所有字段。

在实践中,决定向库用户公开什么以及隐藏什么作为实现细节是一个难题,这需要对库的使用和语义进行详细检查。 如果你真的想隐藏类内部作为实现细节,这里有一些常见的选项:

  • pimpl成语是一种常见的解决方案。 它使您能够像通常那样使用该类,但是实现细节被很好地隐藏了。
  • 将接口提取到具有虚函数的抽象类中,并使用指针(最好是智能指针)来处理对象。

无论如何,标题只是声明,对吗? 它们永远不会传递给编译器。

当您对文件执行#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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM