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

时间:2017-09-14 16:24:30

标签: c++ oop

我的意思是我的真实头文件看起来像这样:

#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();
};

标题只是声明无论如何正确吗?他们永远不会传递给编译器。我已经宣布了用户将使用的内容。

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

4 个答案:

答案 0 :(得分:7)

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

[basic.def.odr]/6

  

a类中可以有多个定义,[...]   程序规定每个定义出现在不同的地方   翻译单元,并提供满足以下要求的定义   要求。鉴于这样一个名为D的实体在多个中定义   翻译单位,然后

     
      
  • D的每个定义应由相同的令牌序列组成;和
  •   

如果您违反此类ODR,您的程序可能会轻易中断。而你的构建系统根本没有义务甚至警告你。

答案 1 :(得分:3)

您无法定义两次课程。它打破了One Definition Rule(ODT)。不幸的是,MyLibrary做到了这一点。

  

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

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

答案 2 :(得分:1)

  

标题只是声明无论如何正确吗?他们永远不会传递给他们   编译器。我已经宣布了用户将使用的内容。

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

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

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

  • pimpl成语是一种常见的解决方案。它使您可以像通常那样使用类,但实现细节很好地隐藏。
  • 使用虚函数将接口解压缩为abstract class,并使用指针(最好是smart pointers)来处理对象。

答案 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,它将携带析构函数的逻辑。