谷歌的C++风格指南says,“当前向声明足够时,不要使用#include”。转发声明或自足标题?
但C++ Coding Standards(萨特和Alexandrescu的),项目#23,“让头文件自给自足的行为负责:。确保每个你写的标题是编译独立的,有它包含的内容依赖于任何头”
哪种策略最好?
谷歌的C++风格指南says,“当前向声明足够时,不要使用#include”。转发声明或自足标题?
但C++ Coding Standards(萨特和Alexandrescu的),项目#23,“让头文件自给自足的行为负责:。确保每个你写的标题是编译独立的,有它包含的内容依赖于任何头”
哪种策略最好?
萨特和Alexandrescu的有关项目#22说:“不要过分依赖:不要#包括定义,当一个向前声明将尽”。
就我个人而言,我同意这种说法。如果在我的类A中,我没有使用类B的任何功能,也没有实例化类B的对象,那么我的代码不需要知道如何创建类B。我只需要知道它存在。
向前声明也打破循环依赖有用...
编辑:我也想了很多的意见,即标记B指出:有时会发生,你不包括文件a.hpp因为它已经包含在你正在包含的文件b.hpp中,即使前向声明足够,它也选择包含a.hpp。如果你停止使用b.hpp中定义的函数,你的代码将不再编译。如果b.hpp的程序员已经使用了前向声明,那就不会发生了,因为您将在代码中的其他地方包含a.hpp ...
很好找!这解决了错误的二分法。 – traal
Sutter和Alexandrescu在这里可能更正确。如果我转发声明class Foo;
由于某种原因存在于bar.h
并且不包括bar.h
,试着找到Foo
的声明(特别是如果代码库很大)时,祝你好运。
我想说这两个陈述是正确的。如果您的头文件只包含一个指针或某个数据类型的引用,那么您只需要一个前向声明。
如果您的头文件包含特定类型的对象,那么您应该在其中定义该类型的头文件中包含该头文件。 Sutter和Alexandrescu的建议是告诉您不要依赖头文件的使用者通过包含所需的类型定义来解决这些引用问题。
您可能还想看看Pimpl Idiom和Compilation firewalls(或C++11 version)。
谢谢,现在我看到我失踪了。头文件通常可以用一个不包含#的东西的前向声明进行编译。只有代码文件仍然需要#include。 – traal
除了其他答案中所涵盖的内容外,还有一些情况是您的必须由于相互依赖性而使用前向声明。
FWIW,如果我只需要一个类型名称,我通常会向前声明它。如果它是一个函数声明,我通常会包含它(尽管这些情况很少见,因为非成员函数很少,并且必须包含成员函数)。
如果它是一个extern "C"
功能,我从来没有向前声明是因为链接器不能告诉我,如果我搞砸了参数类型。
我相信他们都说完全一样的东西。
假设你有一个方法通过引用你的头文件需要一个Bar
,但方法在你的源文件中定义。标题中的前向声明显然足以让标题独立编译。
现在我们来看看用户代码。如果客户端只是传递从其他地方转发的引用,那么他们根本不需要定义Bar
,并且一切都很好。如果他们以某种其他方式使用Bar
,那么该源文件是强制使用包含Bar
,而不是您的标头的内容。事实上,如果你在头文件中包含不需要的Bar
,那么如果客户端不再需要你的包含并将其删除,突然之间他们的其他Bar
代码就会退出工作,因为它们从未将它正确地包含在它们自己的源文件中。
现在假设您的标题使用std::string
。在这种情况下,为了头文件独立编译,您必须包含<string>
,这两个准则都会告诉您这么做(谷歌是因为前向声明不行,而Sutter和Alexandrescu允许头文件独立编译)。
这两个建议是完全是彼此兼容。你可以同时关注他们两个。它们绝不是相互排斥的 - 它不是一个“两个或两个”的情况。
的报头可以使用前向声明,当是所有需要,并且仍然是可编译单机:
// Foo.hpp
class Bar; // Forward declaration
class Foo {
public:
void doSomethingFancy();
private:
Bar *bar;
};
在上述例子中,Foo.hpp
是自给自足的,即使Bar
类是使用前向声明而不是包含Bar.hpp
标头声明的。
依赖管理是在C++中非常重要的:如果你改变一个头文件,这取决于这个头文件所有的翻译单位需要进行编译。这可能非常昂贵。因此,你希望你的头文件是最小的,因为它们不包含任何他们不需要包含的东西。这就是Google的建议。
如果您需要一定的分量,你应该包括组件的标题。但是,您不应该要求除组件的任何内容外的得到的声明。也就是说,每个头文件必须编译而不包含任何其他内容。这是Herb和Andrei给出的建议。请注意,此只有适用于获取声明:如果要使用这些声明中的任何一个,并且需要另一个组件,则可能还需要包含此组件的头文件。
然而,这两个建议一起去!他们都非常有价值,他们应该毫不妥协地跟踪。这基本上意味着,如果您只需要声明的类,那么您更愿意声明一个包含其标题的类。也就是说,如果类仅显示声明,则在指针或引用定义,参数列表或返回类型中,声明的类声明为就足够了。如果您需要更多地了解课程,例如因为它是定义的基类或类的成员,或者使用了一个对象在内联函数中,您需要定义。实际上,如果您需要了解任何成员或班级规模,则需要定义班级。
一个有趣的扭曲是类模板:只有第一个类模板声明可以定义默认参数!但是,类模板可以多次声明。为了在声明类模板时使头文件最小化,您可能希望为涉及的类模板提供特殊的转发头,这些头只会声明类模板以及它的默认参数。然而,这是实施土地的方式...
我更喜欢Google。它降低了复杂性。 –
这两种做法是完全可能的。 –
我不认为他们互相矛盾。应用这两个似乎是一个好主意 – Arvid