【发布时间】:2008-12-17 19:02:10
【问题描述】:
我使用过一些非常大规模的系统,但从未见过需要的订单,但最近遇到了。 STL 或 STD 库甚至 Boost 是否存在某些包含必须按特定顺序出现的情况?
【问题讨论】:
标签: c++ include library-design
我使用过一些非常大规模的系统,但从未见过需要的订单,但最近遇到了。 STL 或 STD 库甚至 Boost 是否存在某些包含必须按特定顺序出现的情况?
【问题讨论】:
标签: c++ include library-design
STL 或 STD 库甚至 Boost 是否存在某些包含必须按特定顺序出现的情况?
对于标准,答案很明确,否。我想 Boost 也是如此,虽然我没有查过。
来自 C 标准:
标准标题可以按任何顺序包含;每一个都可以被多次包含在 一个给定的范围,与只包含一次没有任何不同,除了 包含
<assert.h>的效果取决于NDEBUG的定义(参见7.2)。
C++ 标准有类似的措辞。
我的偏好是标题应该包含它们自己的依赖项,但我曾与认为这是“浪费”的人合作过。在我看来,不让 headers 包含它们的依赖项是一个毫无价值的早期优化。
【讨论】:
这听起来绝对是个糟糕的设计。如果以某种方式需要特定顺序,则库应提供 one 标头,其中包含 正确 顺序中的其他标头。
就 boost 和 STL 而言,我很确定我还没有遇到过这种情况。
【讨论】:
需要以特定顺序指定包含几乎总是表明存在设计问题。减少无意中执行此操作的可能性的一种方法是练习将类的头文件作为第一个#include 包含在实现文件中。
// A.cpp
#include "A.h"
#include "boost/shared_ptr.hpp"
#include <vector>
class A {
// ...
};
这样,例如,如果 A.h 使用没有正确 #include 的向量,A.cpp 将无法编译。
我不记得我在哪里捡到这个了;它可能来自 Lakos 的“Large Scale C++ Design”(一本真正可以使用更新的好书)。
【讨论】:
STL 或 STD 库甚至 Boost 是否存在某些包含必须按特定顺序出现的情况?
我从来没有遇到过这种情况,如果是这样,那么必须尽快通知作者。哦,是的,这是一个非常糟糕的设计。
【讨论】:
将项目级兼容性标头(比如 compat.h)作为任何 .c/.cpp 源文件的 first 标头包含在内是一种常用技术,它定义了一堆必需的宏,例如 __STDC_LIMIT_MACROS , __REENTRANT 和其他项目范围的宏来影响标准头文件的后续行为。
我很久以前第一次看到这种用法是由一位称职的程序员在内部库中使用的。后来我看到 'git'(臭名昭著的 dvcs)项目也使用了这种技术。
【讨论】:
那是“坏事”。已经提到了更好的方法;但我会详细说明。
//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 =(
【讨论】:
如果标题中包含的函数和/或类(例如,A.h)依赖于另一个标题(例如,B.h)中定义的函数和/或类,我更愿意将后者包含在第一个中,而不是强制用户第一个以特定顺序包含两者。
是的:
// 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"
【讨论】:
我喜欢按字母顺序包含标题 - 可以轻松查看我已经完成的工作。
如果一个库因为顺序错误而无法工作,那么它就被破坏了,应该被修复以便与顺序无关。
【讨论】:
如果我没记错的话,对我来说这是一个糟糕的设计,不幸的是,它恰好位于带有 socket/socket2 的 win32 API 中。结果是包含顺序中的错误将触发一组错误,这些错误恰好来自无处可去,并且在依赖项更改定义但代码仍然可以编译的情况下可能难以调试。
在任何其他情况下,您仍然会遇到麻烦。如果您不包含标头 x.h 因为 y.h 已经包含它,那么您的代码依赖于 y.h 对 x.h 的依赖。如果稍后 y.h 被重构并且不再需要 y.h,则删除包含将破坏您的代码库。这是耦合的标志(即使不是在类级别):代码库的一部分中的更改需要传播并扩展到代码的其他部分。
【讨论】:
这可能表明您正在使用 MFC,而这反过来又可能表明设计不佳(开个玩笑……或者是吗?)
(至少,我上次看MFC时,对你在哪里包含<windows.h>真的很挑剔)
【讨论】:
据我所知。这是非常糟糕的做法。不过,我最近在我的代码中遇到了一个 Windows 标头和一些奇怪的界面。
【讨论】:
您应该使用包含保护和前向声明,这样您就不应该对包含标头的顺序有太大问题。
有时仍然需要首先或最后包含标题,不知道为什么。
(例如:在 Source SDK 中)
【讨论】:
是的,在 c++ 中要求包含特定顺序是不良库/头文件设计的标志。
尽管前向声明可能需要包含多个文件才能完全使用一个类。请参见下面的示例:
//啊.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);
}
【讨论】: