你的设计似乎可行。但是,我不会推荐它。而且您没有提到模板或标准容器。
我的感觉是
实际上很重要(出于效率原因),有很多(通常很小)inline
函数,特别是内联成员函数(如 getter、setter 等),通常包含在它们的class
Class {
....}
定义中。
因此,一些成员函数应该是inline
,或者在类内部
class Foo {
int _x;
Foo(int x) : _x(x) {};
~Foo() { _x=0; };
int f(int d) const { return _x + d; };
}
然后所有的构造函数Foo::Foo(int)
、析构函数Foo::~Foo
和成员函数int Foo::f(int)
都被内联
或在课程之后(对于机器生成的代码通常更容易),例如
class Foo {
int _x;
inline Foo(int x);
inline ~Foo();
inline int f(int d) const;
};
Foo::Foo(int x) { _x = x; };
Foo::~Foo() { _x = 0; };
int Foo::f(int d) const { return _x+d; };
在这两种情况下,出于效率原因,您都需要内联(或者可能是链接时间优化,例如gcc -flto -O
用于编译和链接)。
编译器只有在知道函数的定义(它们的主体)时才能内联函数。
然后,每次你#include
定义一些类。您需要以某种方式编译该内联函数定义。要么将它放在同一个标题中,要么该标题本身应该是#include
其他文件(提供内联函数的定义)
通常,尤其是在使用标准 C++ 库(以及使用标准容器,例如#include <vector>
)时,您会得到很多系统头文件(间接包括在内)。在实践中,您不需要非常小的实现文件(即每个文件有几十行源代码是不切实际的)。
同样,现有的 C++ 框架库会提取大量(间接)标头(例如#include <QtGui>
,带来大量代码)。
我建议至少拥有一千行的C++ 源文件(*.hh
或者)。*.cc
看看预处理代码的大小,例如g++ -H -C -E
……在实践中你会感到害怕:即使编译一个只有几十行源代码的小 C++ 文件,你也会有成千上万的预处理源代码行。
因此,我对千行源文件的建议:任何使用 C++ 标准库或某些 C++ 框架库(Boost、Qt)的较小文件都会从间接包含的文件中提取大量源代码行。
另请参阅此答案,为什么 Google(使用 D.Novillo)努力将预解析的标头添加到 GCC,为什么 LLVM/Clang(使用 C.Latner)需要 C 和 C++ 中的模块。以及为什么 Ocaml、Rust、Go ……有模块……
您还可以查看 GCC 生成的 GIMPLE 表示,或者使用MELT 探针(MELT 是一种扩展 GCC 的特定领域语言,探针是一个简单的图形界面,用于检查 GCC 的一些内部表示,例如 Gimple),或者使用-fdump-tree-all
选项GCC(小心:该选项会产生数百个转储文件)。您也可以将-ftime-report传递给 GCC,以便在编译 C++ 代码时更多地了解它在哪里浪费时间。
对于机器生成的 C++ 代码,我建议生成更少的文件,但要使它们更大。生成数千个几十行的小 C++ 文件效率低下(使总构建时间太长):编译器将花费大量时间#include
一次又一次地解析相同的 -d 系统头文件,并实例化相同的模板化类型(例如,当使用标准容器时)很多次。
请记住,C++ 允许每个源文件有多个类(与 Java 相反(内部类除外))。
此外,如果生成了所有 C++ 代码,则您实际上不需要生成头文件(或者您可能会生成一个 big *.hh
),因为您的生成器应该知道在每个生成中真正使用了哪些类和函数,*.cc
并且可以在其中生成只归档那些有用的声明和内联函数定义。
PS:请注意,inline
(like register
) 只是对编译器的一个(有用的)提示。它可以避免内联标记的函数inline
(即使是隐含的,在class
定义内部时)。它也可能内联一些未标记的函数inline
。但是,编译器需要知道函数的主体才能内联它。