有充分的理由在 h 文件而不是 cpp 文件中使用 include guards 吗?
Is there a good reason to use include guards in h files instead of cpp files?
例如,根据我对 C++ 和 clang++ 的基本理解,我不明白为什么以下设置不是更可取的:
似乎使用 headers 的变体会产生更多的输出,需要更具体的编译器设置,并且需要在函数声明中重复。
clang++ --std=c++2a Start.cpp -o Start.o; ./Start.o
Start.cpp
#include "A.cpp"
#include "B.cpp"
int main (
int argc,
char** argv
) {
A();
B();
return 0;
}
A.cpp
#ifndef A_H
#define A_H
#include <stdio.h>
#include "C.cpp"
void A() {
printf("A\n");
C();
}
#endif
B.cpp
#ifndef B_H
#define B_H
#include <stdio.h>
#include "C.cpp"
void B() {
printf("B\n");
C();
}
#endif
C.cpp
#ifndef C_H
#define C_H
#include <stdio.h>
void C() {
printf("C\n");
}
#endif
如果您 link 该目标文件与另一个包含其中一个 cpp 文件的目标文件,则该程序将违反一个定义规则,因为这些文件包含非内联函数的定义。
此外,通常将cpp文件和link一起编译。如果您编译这些 cpp 文件中的任何一个,同时将其包含到另一个翻译单元中,您将遇到违反 ODR 的情况。
要解决这个问题,如果您将一个文件包含到其他文件中,那么您通常应该确保包含的文件(即头文件)不包含任何在包含到多个翻译单元中时会违反 ODR 的内容。
此外,最好遵循常用的命名方案。对头文件使用 cpp 扩展名与所有常见的命名方案相反。
Is there a good reason to use include guards in h files instead of cpp files?
无论您如何命名头文件,您都应该始终在所有头文件中使用 include guard。 (虽然从技术上讲,某些头球不需要头球后卫,但简单地使用头球后卫比跟踪是否需要它更容易)。
现在,如果您想知道是否可以将所有函数定义放入一个翻译单元中:是的,可以。这有优点也有缺点:
单个 TU 的优点:由于无需重复编译模板和其他内联函数,因此从头开始编译时间更快。也有更好的优化,因为没有翻译单元边界会阻止优化 - 这种优势被跨 TU 边界工作的 link 时间优化削弱了。
缺点:对程序的任何更改只会导致重新编译修改后的翻译单元。当整个程序只有一个翻译单元时,那么任何更改,无论多么小,都需要重新编译整个程序。这是非常不希望的。 link 时间优化减少了这个缺点,这可能导致 linking 非常慢,以至于从重用翻译单元节省的时间变得微不足道。
例如,根据我对 C++ 和 clang++ 的基本理解,我不明白为什么以下设置不是更可取的:
似乎使用 headers 的变体会产生更多的输出,需要更具体的编译器设置,并且需要在函数声明中重复。
clang++ --std=c++2a Start.cpp -o Start.o; ./Start.o
Start.cpp
#include "A.cpp"
#include "B.cpp"
int main (
int argc,
char** argv
) {
A();
B();
return 0;
}
A.cpp
#ifndef A_H
#define A_H
#include <stdio.h>
#include "C.cpp"
void A() {
printf("A\n");
C();
}
#endif
B.cpp
#ifndef B_H
#define B_H
#include <stdio.h>
#include "C.cpp"
void B() {
printf("B\n");
C();
}
#endif
C.cpp
#ifndef C_H
#define C_H
#include <stdio.h>
void C() {
printf("C\n");
}
#endif
如果您 link 该目标文件与另一个包含其中一个 cpp 文件的目标文件,则该程序将违反一个定义规则,因为这些文件包含非内联函数的定义。
此外,通常将cpp文件和link一起编译。如果您编译这些 cpp 文件中的任何一个,同时将其包含到另一个翻译单元中,您将遇到违反 ODR 的情况。
要解决这个问题,如果您将一个文件包含到其他文件中,那么您通常应该确保包含的文件(即头文件)不包含任何在包含到多个翻译单元中时会违反 ODR 的内容。
此外,最好遵循常用的命名方案。对头文件使用 cpp 扩展名与所有常见的命名方案相反。
Is there a good reason to use include guards in h files instead of cpp files?
无论您如何命名头文件,您都应该始终在所有头文件中使用 include guard。 (虽然从技术上讲,某些头球不需要头球后卫,但简单地使用头球后卫比跟踪是否需要它更容易)。
现在,如果您想知道是否可以将所有函数定义放入一个翻译单元中:是的,可以。这有优点也有缺点:
单个 TU 的优点:由于无需重复编译模板和其他内联函数,因此从头开始编译时间更快。也有更好的优化,因为没有翻译单元边界会阻止优化 - 这种优势被跨 TU 边界工作的 link 时间优化削弱了。
缺点:对程序的任何更改只会导致重新编译修改后的翻译单元。当整个程序只有一个翻译单元时,那么任何更改,无论多么小,都需要重新编译整个程序。这是非常不希望的。 link 时间优化减少了这个缺点,这可能导致 linking 非常慢,以至于从重用翻译单元节省的时间变得微不足道。