是否要求c ++中#includes的某个订单是库/标题设计错误的标志?

时间:2008-12-17 19:02:11

标签: c++ include library-design

我使用了一些非常大规模的系统,从未见过必需的订单,但最近遇到了它。 STL或STD库甚至Boost是否存在某些包含必须按特定顺序出现的情况?

13 个答案:

答案 0 :(得分:28)

  

STL或STD库甚至Boost是否存在某些包含必须按特定顺序排列的情况?

对于标准,答案是强调,。我想起了Boost也是如此,虽然我没有查看过。

来自C标准:

  

标准标题可以包含在任何顺序中;每个可以包含多次   一个给定的范围,没有任何效果不同于只包括一次,除了   包含<assert.h>的效果取决于NDEBUG的定义(见7.2)。

C ++标准有类似的措辞。

我的偏好是标题应该包含他们自己的依赖关系,但我与那些认为这是“浪费”的人一起工作。在我看来,没有标题包含它们的依赖关系是一个毫无价值的早期优化。

答案 1 :(得分:9)

这绝对听起来像是一个糟糕的设计。如果以某种方式需要特定订单,则库应提供一个标头,其中包含正确顺序中的其他标头。

至于提升和STL,我很确定我还没有遇到过这种情况。

答案 2 :(得分:9)

需要以特定顺序指定包含几乎总是表示设计问题。减少无意中执行此操作的可能性的一种方法是进入将类的头文件包含为实现文件中的第一个#include的做法。

// A.cpp
#include "A.h"
#include "boost/shared_ptr.hpp"
#include <vector>

class A {
// ...
};

这种方式,例如,如果A.h使用没有正确#include的向量,A.cpp将无法编译。

我不记得我把它拿到了哪里;它可能来自Lakos的“大规模C ++设计”(一本可以真正使用更新的好书)。

答案 3 :(得分:3)

  

STL或STD库甚至Boost是否存在某些包含必须按特定顺序排列的情况?

我从未遇到过这种情况,如果是这样的话,必须尽快通知作者。哦,是的,这是一个非常糟糕的设计。

答案 4 :(得分:3)

这是一件“坏事”。提到了一种更好的方法;但我会详细说明。

//a.h
#ifndef _A_H_
#define _A_H_

//... code ...

#endif
// -----------------
//b.h
#ifndef _B_H_
#define _B_H_
#include a.h

//... code ...

#endif
// -----------------
//main.cpp Try 1
#include "b.h" //<- okay!  b includes a, then does b
// -----------------
//main.cpp Try 2
#include "a.h" //<- includes a
#include "b.h" //<- okay!  b includes a, but skips redefining it, then does b
// -----------------
//main.cpp Try 3
#include "b.h" //<- b includes a, then does b
#include "a.h" //<- okay!  a skips redefining itself!
// -----------------
//main.cpp Try 4
#include "a.h" //<- fail!  b is not included anywhere =(

答案 5 :(得分:3)

将项目级兼容性标头(比如compat.h)作为任何.c / .cpp源文件的第一个标头,这是一种常见的技术,它定义了一堆必需的宏,如__STDC_LIMIT_MACROS ,__ REENTRANT和其他项目范围的宏会影响标准标题的后续行为。

我很久以前就是一位有能力的程序员在内部库中看到这种用法。后来我看到'git'(臭名昭着的dvcs)项目也使用了这种技术。

答案 6 :(得分:2)

如果标题中包含的函数和/或类(例如,Ah)依赖于另一个标题中定义的函数和/或类(例如,Bh),我更喜欢在后者中包含后者,而不是强制用户第一个以特定顺序包括两者。

是:

// A.h
#pragma once
// or the #ifndef trick
#include "B.h"

// A.cpp
#include "A.h"

没有

// A.h
#pragma once
// or the #ifndef trick
//#include "B.h"

// A.cpp
#include "B.h"
#include "A.h"

答案 7 :(得分:1)

我喜欢按字母顺序排列标题 - 让我们很容易看到我已经完成的工作。

如果lib不能正常工作,因为它的顺序错误,那么它就会被破坏,应该被修复以便与顺序无关。

答案 8 :(得分:1)

对我来说,这是一个糟糕的设计,不幸的是,碰巧是在带有socket / socket2包含的win32 API中,如果我没记错的话。结果是包含顺序中的错误将触发一组恰好来自无处的错误,并且在依赖项更改定义但代码仍然编译的情况下可能难以调试。

在任何其他情况下,您仍然会遇到麻烦。如果你没有包含头x.h,因为y.h已经包含它,那么你的代码依赖于对x.h的y.h依赖。如果稍后y.h被重构并且它不再需要y.h,那么删除include会破坏你的代码库。这是耦合的标志(即使不是在类级别):代码库的一部分中的更改需要传播并扩展到代码的其他部分。

答案 9 :(得分:1)

这或许表明你正在使用MFC,这可能反过来表明设计不好(开玩笑......或者是它?)

(至少,上次我看过MFC时,你对<windows.h>所包含的内容非常挑剔

答案 10 :(得分:0)

据我所知。这是非常糟糕的做法。我最近遇到了一个Windows标题和我的代码中的一些奇怪的界面。

答案 11 :(得分:0)

你应该使用包含警戒和转发声明,这样你就不应该对包含标题的顺序有太多问题。

有时仍需要首先包含标题或最后包含标题,不知道为什么 (例如:在Source SDK中)

答案 12 :(得分:0)

是的,要求c ++中的包含的某个顺序是库/标题设计错误的标志。

虽然前向声明可能需要包含多个文件才能完全使用类。见下面的例子:

// A.h

class B; // forward declaration

class A
{
    void doStuff(const B& b);
};

// main.cpp

#include <A.h>
#include <B.h>

int main()
{
    A a;
    B b;
    a.doStuff(b);
}