清理旧版代码"标头意大利面条"

4 c++ legacy-code

任何推荐的清理"标头意大利面条"的做法都会导致编译时间极慢(Linux/Unix)?

与GCC有"#pragma once"等价吗?
(发现有关此事的相互矛盾的消息)

谢谢.

Ass*_*vie 8

假设你熟悉"包含警卫"(标题开头的#ifdef ......),另外一种加快构建时间的方法是使用外部包含警卫.它在" 大规模C++软件设计 "中进行了讨论.这个想法是经典包括守卫,不像#pragma一次,不要让你从第二次开始忽略头部所需的预处理器解析(即它仍然需要解析并寻找包含守卫的开始和结束.外部包含警卫你将#ifdef放在#include线本身周围.

所以它看起来像这样:

#ifndef MY_HEADER
#include "myheader.h"
#endif
Run Code Online (Sandbox Code Playgroud)

当然在H档案中你有经典的包括后卫

#ifndef MY_HEADER
#define MY_HEADER

// content of header

#endif
Run Code Online (Sandbox Code Playgroud)

这样,myheader.h文件甚至不被预处理器打开/解析,它可以在大型项目中节省大量时间,特别是当头文件位于共享远程位置时,就像它们有时一样.

再次,这一切都在那本书中.心连心


Ric*_*ard 6

如果你想做一个完整的清理并有时间去做,那么最好的解决方案是删除所有文件中的所有#includes(除了显而易见的,例如abc.cpp中的abc.h)然后编译项目.添加必要的前向声明或标题以修复第一个错误,然后重复,直到您干净利落.

这不会解决可能导致包含问题的潜在问题,但它确实确保只包含必需的包含问题.


jas*_*ray 5

我读到 GCC 认为#pragma once已弃用,尽管甚至#pragma once只能做这么多来加快速度。

要尝试解开#include意大利面条,您可以查看doxygen。它应该能够生成包含标题的图表,这可能会给您带来简化事物的优势。我无法立即回忆起详细信息,但图形功能可能需要您安装GraphViz并告诉 doxygen 可以找到 GraphViz 的 dotty.exe 的路径。

如果编译时间是您最关心的问题,您可能会考虑的另一种方法是设置Precompiled Headers


pae*_*bal 5

理查德是对的(为什么他的解决方案被记下来?)。

无论如何,所有 C/C++ 标头都应使用内部包含防护。

这说,要么:

1 - 你的遗留代码不再被真正维护,你应该使用预编译头(这是一个黑客,但是嘿......你需要的是加速你的编译,而不是重构未维护的代码)

2 - 您的旧代码仍然有效。然后,您可以使用预编译标头和/或守卫/外部守卫作为临时解决方案,但最终,您需要删除所有包含文件(一次一个 .C 或 .CPP),然后编译每个 .C 或 .CPP 文件。 C 或 .CPP 文件一次一个,使用前向声明或必要时包含来更正其包含(或者甚至将大包含分解为较小的包含以确保每个 .C 或 .CPP 文件仅获得其所需的标头)。无论如何,测试和删除过时的包含是项目维护的一部分,所以......

我自己对预编译头的经验并不完全是一个好的经验,因为有一半的时间,编译器找不到我定义的符号,所以我尝试了完整的“清理/重建”,以确保它不是预编译头那已经过时了。所以我的猜测是将它用于您甚至不会接触的外部库(例如 STL、C API 标头、Boost 等)。不过,我自己的经验是使用 Visual C++ 6,所以我猜(希望?)他们现在做对了。

现在,最后一件事:标头应该始终是自给自足的。这意味着如果标头的包含取决于包含的顺序,那么就会遇到问题。例如,如果你可以写:

#include "AAA.hpp"
#include "BBB.hpp"
Run Code Online (Sandbox Code Playgroud)

但不是:

#include "BBB.hpp"
#include "AAA.hpp"
Run Code Online (Sandbox Code Playgroud)

因为 BBB 依赖于 AAA,那么您所拥有的只是您从未在代码中承认的依赖项。不通过定义确认它只会使您的编译成为一场噩梦。BBB 也应该包括 AAA(即使它可能会慢一些:最后,前向声明无论如何都会清除无用的包含,因此您应该有一个更快的编译计时器)。