我读到过,使用#pragma一次可以优化编译器,从而加快编译速度。我知道这是不标准的,因此可能会带来跨平台兼容性问题。
非windows平台(gcc)上的大多数现代编译器都支持这一点吗?
我希望避免平台编译问题,但也希望避免备用保护的额外工作:
#pragma once
#ifndef HEADER_H
#define HEADER_H
...
#endif // HEADER_H
我应该担心吗?我还需要在这上面花费更多的精力吗?
我读到过,使用#pragma一次可以优化编译器,从而加快编译速度。我知道这是不标准的,因此可能会带来跨平台兼容性问题。
非windows平台(gcc)上的大多数现代编译器都支持这一点吗?
我希望避免平台编译问题,但也希望避免备用保护的额外工作:
#pragma once
#ifndef HEADER_H
#define HEADER_H
...
#endif // HEADER_H
我应该担心吗?我还需要在这上面花费更多的精力吗?
当前回答
如果我们使用msvc或Qt(到Qt 4.5),因为GCC(到3.4),msvc都支持#pragma一次,我可以看到没有理由不使用#pragma一次。
源文件名通常与类名相同,我们知道,有时我们需要重构,重命名类名,那么我们也必须改变#include XXXX,所以我认为手动维护#include xxxxx不是一个聪明的工作。即使使用Visual Assist X扩展,维护“xxxx”也不是一项必要的工作。
其他回答
性能的好处在于,一旦读取了#pragma,就不必重新打开文件。使用保护,编译器必须打开文件(这可能会浪费时间)来获得它不应该包含它的内容的信息。
这只是理论上的,因为对于每个编译单元,一些编译器将自动不打开没有任何读取代码的文件。
无论如何,并不是所有编译器都是这样,所以理想情况下,#pragma一旦出现就必须避免,因为跨平台代码根本就不是标准的/没有标准化的定义和效果。然而,实际上,它确实比守卫要好。
最后,在不检查每个编译器的行为的情况下,确保编译器具有最佳速度的更好建议是一次性使用pragma和守卫。
#ifndef NR_TEST_H
#define NR_TEST_H
#pragma once
#include "Thing.h"
namespace MyApp
{
// ...
}
#endif
这样你就可以获得两者的优势(跨平台和帮助编译速度)。
由于打字时间较长,我个人使用一种工具来帮助生成所有这些信息(Visual Assist X)。
我不知道有什么性能上的好处,但它确实有效。我在我所有的c++项目中都使用它(当然我使用的是MS编译器)。我发现它比使用更有效
#ifndef HEADERNAME_H
#define HEADERNAME_H
...
#endif
它做同样的工作,并且不使用额外的宏填充预处理器。
GCC在3.4版正式支持#pragma。
如果我们使用msvc或Qt(到Qt 4.5),因为GCC(到3.4),msvc都支持#pragma一次,我可以看到没有理由不使用#pragma一次。
源文件名通常与类名相同,我们知道,有时我们需要重构,重命名类名,那么我们也必须改变#include XXXX,所以我认为手动维护#include xxxxx不是一个聪明的工作。即使使用Visual Assist X扩展,维护“xxxx”也不是一项必要的工作。
#pragma once确实有一个缺点(除了不是标准的),那就是如果你在不同的位置有相同的文件(我们有这样的情况是因为我们的构建系统会四处复制文件),那么编译器会认为这些是不同的文件。
主要的区别是编译器必须打开头文件来读取包含保护。相比之下,pragma只会导致编译器跟踪该文件,并且在遇到针对同一文件的另一个include时不执行任何文件IO。虽然这听起来可以忽略不计,但它可以很容易地扩展到大型项目,特别是那些没有好的标题包括学科的项目。
也就是说,现在的编译器(包括GCC)足够聪明,可以像对待pragma一样对待include守卫。例如,他们不打开文件,避免文件IO惩罚。
在不支持pragma的编译器中,我见过手动实现有点麻烦。
#ifdef FOO_H
#include "foo.h"
#endif
我个人喜欢#pragma once方法,因为它避免了命名冲突和潜在的拼写错误的麻烦。相比之下,它的代码也更加优雅。也就是说,对于可移植的代码,如果编译器不同意,那么两者都使用也无妨。