关于编码风格:头文件中的C ++代码

C++ code in header files

我个人的C++风格总是把类声明放在一个包含文件中,并且在.CPP文件中定义,非常类似于洛基对C++头文件的答案,代码分离。诚然,我喜欢这种风格的部分原因可能与我花在编写modula-2和ada上的所有时间有关,这两个模块都有一个与规范文件和正文文件类似的方案。

我有一个同事,比我更懂C++,他坚持认为,所有的C++声明都应该尽可能包含头文件中的定义。他并不是说这是一种有效的替代风格,甚至是稍微好一点的风格,而是这是一种新的普遍接受的风格,现在每个人都在使用C++。

我不再像以前那样灵活了,所以我并不急着要爬上他的车,直到我看到更多的人和他在一起。那么这个成语到底有多普遍呢?

只是为了给答案一些结构:它是现在的方式,非常常见,有点常见,不常见,还是发疯了?


您的同事是错误的,通常的方法是将代码放在.cpp文件(或您喜欢的任何扩展名)中,并将声明放在头中。

有时将代码放在头中有一些好处,这样编译器就可以更巧妙地进行内联。但同时,它也会破坏您的编译时间,因为每次编译器包含所有代码时都必须对其进行处理。

最后,当所有的代码都是头时,拥有循环对象关系(有时是需要的)通常是很烦人的。

总之,你是对的,他错了。

编辑:我一直在考虑你的问题。有一种情况是他说的是真的。模板。许多较新的"现代"库(如Boost)大量使用模板,并且通常是"仅限标题"。但是,只有在处理模板时才应该这样做,因为这是处理模板时唯一的方法。

编辑:有些人希望更清楚一点,下面是一些关于编写"仅标题"代码的缺点的想法:

如果你四处搜索,你会看到很多人试图找到一种方法来减少处理boost时的编译时间。例如:如何使用BoostASIO减少编译时间,它可以看到一个包含Boost的1K文件的14秒编译。14世纪似乎不是"爆炸性的",但它肯定比典型的要长得多,而且加起来很快。处理大型项目时。只有头的库确实以一种相当可测量的方式影响编译时间。我们只是容忍它,因为增强是如此有用。

此外,还有许多事情不能只在头文件中完成(即使Boost也有一些库需要链接到某些部分,如线程、文件系统等)。一个主要的例子是,你不能在头上只有libs的简单全局对象(除非你诉诸于单例的可憎),因为你会遇到多个定义错误。注意:C++ 17的内联变量将使这个特殊的例子在将来可行。

最后一点是,当使用Boost作为只包含头代码的示例时,往往会忽略大量细节。

Boost是库,而不是用户级代码。所以不会经常改变。在用户代码中,如果将所有内容都放在头中,每一个小的更改都将导致您必须重新编译整个项目。这是一种巨大的浪费时间(对于那些从编译到编译都没有改变的库来说,情况并非如此)。当您在头/源之间进行拆分时,最好使用前向声明来减少include,这样在一天中进行合计时可以节省重新编译的时间。


一天C++的人同意了,羔羊将与狮子躺在一起,巴勒斯坦人将拥抱以色列人,猫狗将被允许结婚。

此时.h和.cpp文件之间的分离大多是任意的,这是很久以前编译器优化的一个残余。在我看来,声明属于头部,定义属于实现文件。但是,这只是习惯,不是宗教。


头中的代码通常是一个坏主意,因为当您更改实际代码而不是声明时,它强制重新编译包含头的所有文件。它还将减慢编译速度,因为您需要解析包含头部的每个文件中的代码。

在头文件中包含代码的一个原因是,关键字inline正常工作以及在使用其他cpp文件中实例化的模板时通常都需要它。


可能告诉你的同事是一个概念,即大多数C++代码应该被模板化以允许最大的可用性。如果它是模板化的,那么所有东西都需要放在一个头文件中,这样客户机代码就可以看到它并实例化它。如果它对增强和STL足够好的话,对我们来说就足够好了。

我不同意这一观点,但这可能是它的来源。


我认为你的公司和你是聪明人,也是正确的。P></

found that the useful things into the把一切的headers:is thatP></

  • 写作不need for &;headers和源同步。P></

  • the structure is plain and the dependencies队不圆式编码器,在"美好"的结构。P></

  • 方便的便携式,嵌入式和新项目。P></

  • 我同意与compiling时间问题,但我认为,我们应该注意:P></

  • the change of源码文件likely to change the header是超文件which to the Whole recompiled导致项目好了。P></

  • 速度比以前快多了compiling is。如果你有一个项目与新建好长时间和高频率,它可以indicates that has flaws设计你的项目。seperate into the different项目任务和模块可以avoid this的问题。P></

  • 我只是想支持你的lastly公司工人,只是我个人的观点。P></


    通常我会将一些琐碎的成员函数放入头文件中,以允许它们被内联。但是要将整个代码体放在那里,只是为了与模板保持一致?那是普通的坚果。

    记住:愚蠢的一致性是小头脑的无赖。


    as托玛士说,你的头应该最小。将扩大到全面的好位。P></

    使用的personally 4 types of C++档案在我的项目:P></

    • 公共的:
    • 学院:欧洲房子前向头文件等文件,get the forwarding declarations that this will appear in the header。
    • 文件标题:this if the前向头文件包括任何和一切,我宣布,希望(defines公共类和茶…)
    • 私人:
    • 私人文件的标题标题:this is reserved for the implementation,它包括表头和说的辅助函数/结构(for the pimpl for example前predicates)。如果跳过不必要的民族。
    • 源文件:包括报头(header if the私人或私人不defines(header)和一切非模板……)

    furthermore this with another,偶规则的定义是:You can do not宣布了。虽然有合理的课程(AM)在使用pimpl到处是那么hassle)。P></

    我喜欢在EN均值#include前馈指令申报过安在我当我离开headers get with them。P></

    最后,使用规则也可视范围:limit the of as as possible的符号,我知道that they do not the outer pollute范围。P></

    把它altogether:P></

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    // example_fwd.hpp
    // Here necessary to forward declare the template class,
    // you don't want people to declare them in case you wish to add
    // another template symbol (with a default) later on
    class MyClass;
    template <class T> class MyClassT;

    // example.hpp
    #include"project/example_fwd.hpp"

    // Those can't really be skipped
    #include <string>
    #include <vector>

    #include"project/pimpl.hpp"

    // Those can be forward declared easily
    #include"project/foo_fwd.hpp"

    namespace project { class Bar; }

    namespace project
    {
      class MyClass
      {
      public:
        struct Color // Limiting scope of enum
        {
          enum type { Red, Orange, Green };
        };
        typedef Color::type Color_t;

      public:
        MyClass(); // because of pimpl, I need to define the constructor

      private:
        struct Impl;
        pimpl<Impl> mImpl; // I won't describe pimpl here :p
      };

      template <class T> class MyClassT: public MyClass {};
    } // namespace project

    // example_impl.hpp (not visible to clients)
    #include"project/example.hpp"
    #include"project/bar.hpp"

    template <class T> void check(MyClass<T> const& c) { }

    // example.cpp
    #include"example_impl.hpp"

    // MyClass definition

    lifesaver here is that the most of the Times is the only necessary useless前馈标题:房子和我templateof typedefor is the header)执行;P></


    为了增加乐趣,您可以添加包含模板实现(包含在.hpp中)的.ipp文件,而.hpp包含接口。

    除了模板化的代码(取决于项目,这可能是大多数或少数文件)之外,还有正常的代码,这里最好将声明和定义分开。在需要时还提供转发声明-这可能会影响编译时间。


    通常,在编写一个新类时,我会将所有代码放在该类中,因此不必在另一个文件中查找它。在一切正常工作之后,我将方法体分解为cpp文件,将原型保留在hpp文件中。


    如果这种新方法真的是这样的话,我们在项目中可能会遇到不同的方向。

    因为我们试图避免头中所有不必要的东西。这包括避免头部级联。头中的代码很可能需要包含一些其他头,这将需要另一个头等等。如果我们被迫使用模板,我们尽量避免在头上乱放太多的模板内容。

    此外,我们还使用"不透明指针"模式(如果适用)。

    通过这些实践,我们可以比大多数同行更快地完成构建。是的…更改代码或类成员不会导致巨大的重建。


    我个人在我的头文件中这样做:

    1
    2
    3
    // class-declaration

    // inline-method-declarations

    我不喜欢将方法的代码与类混合在一起,因为我发现快速查找东西很困难。

    我不会将所有方法都放在头文件中。编译器将(通常)无法内联虚拟方法,并且(可能)只内联没有循环的小方法(完全取决于编译器)。

    在类中执行方法是有效的…但从可读性的角度来看,我不喜欢。将这些方法放在头中确实意味着,如果可能的话,它们将被内联。


    imho,他只有在做模板和/或元编程时才有价值。已经提到了很多原因,您将头文件限制为仅声明。他们只是…标题。如果您想包含代码,可以将其编译为库并链接起来。


    我将所有实现从类定义中去掉。我想把doxygen注释从类定义中去掉。


    我认为这是个荒谬的,你把所有的绝对定义一个函数的头文件。为什么?因为the header文件is used as the public interface to your class。这是"the of the"黑盒"。P></

    当你看to need to how to a级参考使用它,你应该看看the header文件。the header of a list文件应该给什么(commented can do to describe the details of how to each函数的使用),和它应该包括list of the成员变量。EN should not how each function is包括个体要实现,因为这是不必要的民族在船载of the only clutters标题信息和文件。P></


    这真的取决于系统的复杂性和内部约定吗?

    目前,我正在研究一个极其复杂的神经网络模拟器,我期望使用的公认风格是:

    classname.h中的类定义
    classnamecode.h中的类代码
    classname.cpp中的可执行代码

    这将用户构建的模拟从开发人员构建的基类中分离出来,并且在这种情况下工作得最好。

    但是,我会惊讶地看到人们在一个图形应用程序或任何其他应用程序中这样做,其目的不是为用户提供代码库。


    在模板队列should be only选项。在除了定义线分开从that should be在.cpp.argument for this be the best which the STD库会implementations后续规则一样。你会disagree not the developers STD库会对这是正确的。P></


    我认为你是正确的人共He does not as Long as the process to write在Enter the executable队列的头。正确的平衡,我认为,indicated by the path is to后续gnat ADA where the .ads给adequate接口定义文件在perfectly for its of the package for users and its的孩子。P></

    顺便特德,你有有看最近的论坛在线this to the question to the on the Ada图书馆结合剪辑你wrote which is and several年针(没有更多可用的Web pages是现在闭相关)。如果是自制的安老版本的剪辑,这是结合好的人愿意使用start to the example for inference安发动机在2012年由Ada程序。P></