A「;无来源“;C++;习语 我正在开发一个相当大的C++支持库,并且发现自己正在走向一个只头文件的方法。在C++中,这几乎是有效的,因为您可以实现类中定义的位置。对于模板化方法,实现无论如何都必须在同一个文件中,因此我发现只保留带有定义的实现要容易得多

A「;无来源“;C++;习语 我正在开发一个相当大的C++支持库,并且发现自己正在走向一个只头文件的方法。在C++中,这几乎是有效的,因为您可以实现类中定义的位置。对于模板化方法,实现无论如何都必须在同一个文件中,因此我发现只保留带有定义的实现要容易得多,c++,idioms,header-only,C++,Idioms,Header Only,但是,有时必须使用“来源”。仅举一个例子,有时会出现循环依赖,实现必须在类定义之外编写。我是这样处理的: //part of libfoo.h class Bar { void CircularDependency(void); }; #ifdef LIBFOO_COMPILE_INLINE void Bar::CircularDependency(void) { //... } #endif 然后,使用libfoo的项目将在main.cpp中执行以下操作: //main.cpp #

但是,有时必须使用“来源”。仅举一个例子,有时会出现循环依赖,实现必须在类定义之外编写。我是这样处理的:

//part of libfoo.h
class Bar
{
  void CircularDependency(void);
};

#ifdef LIBFOO_COMPILE_INLINE
void Bar::CircularDependency(void)
{
  //...
}
#endif
然后,使用libfoo的项目将在main.cpp中执行以下操作:

//main.cpp
#define LIBFOO_COMPILE_INLINE
#include "libfoo.h"
以及在任何其他.cpp中:

//other.cpp
#include "libfoo.h"
关键是编译内联部分只编译一次(在main.cpp中)

最后,我的问题是:这个习惯用法或任何其他以这种方式工作的项目有名字吗?这似乎是实现和定义被模板和类方法模糊的自然结果。还有:这是一个坏主意,或者它可能无法很好地扩展,有什么原因吗


旁白:我知道很多程序员都有很好的理由,喜欢他们的头类似于接口,而不是实现,但是IMHO文档生成器更适合于描述接口,因为我喜欢将私有成员全部隐藏在一起:-)

如果循环依赖性问题在定义
Bar::circulardependence()时得到解决,您应该能够使用
inline
关键字
将显示在
libfoo.h
标题中:

//part of libfoo.h
class Bar
{
  void CircularDependency(void);
};


// other stuff that 'resolves' the circular dependency
// ...
// ...

inline void Bar::CircularDependency(void)
{
  //...
}

这将使您的库更易于使用(用户不需要处理这样一个事实,即
LIBFOO\u COMPILE\u INLINE
需要在包含头的一个位置定义)。

循环依赖关系不是真正的问题,因为您可以转发函数是内联的。不过,我并不真的建议这样做:虽然“无源代码”方法最初使使用来自某个地方的库变得更容易,但它会导致更长的编译时间和文件之间更紧密的耦合。在一个庞大的源代码库中,这实际上扼杀了在合理时间内构建代码的希望。当然,庞大的程序只从几百万行代码开始,但谁会在乎那些琐碎的程序呢。。。?(是的,我工作的地方有好几千万行代码被构建到单个可执行文件中)

我认为这是个坏主意的原因

编译时间的增加

每个包含头文件的单独编译单元都应该编译所有头文件以及源文件本身。这可能会增加编译时间,并且在测试代码时可能会令人沮丧,只需做一些小的更改。有人可能会说编译器可能会对其进行优化,但在我看来,它无法对其进行超过一点的优化

更大的代码段

如果所有函数都是内联编写的,这意味着编译器必须将所有代码放在调用函数的任何位置。这将破坏代码段,并将影响程序的加载时间,程序将占用更多内存

在紧密耦合的客户端代码中创建依赖关系

无论何时更改实现,都应该更新每个客户端(通过重新编译代码)。但是,如果已将实现放在独立的共享对象(.so或.dll)中,则客户端应该只链接到新的共享对象

我也不知道为什么会这样做

//main.cpp
#define LIBFOO_COMPILE_INLINE
#include "libfoo.h"
如果必须这样做,他可以简单地将实现代码放在main.cpp中。无论如何,您只能在一个编译单元中定义LIBFOO_COMPILE_INLINE。否则,您将复制定义


实际上,我对开发一种编写内聚模板代码的习惯用法非常感兴趣。将来,C++编译器应该支持编写内聚模板。我的意思是,无论何时修改模板实现,客户机都不必重新编译代码。

为什么不像其他人一样将定义放在Cpp文件中,而不是执行一些容易出错的技巧呢?这对我来说似乎不是很自然。如果您想编写Java,那么就使用Java。将他们的风格导入C++似乎是想继续学习你的原语言。关于学习如何以适合语言的风格编写代码的文章很多。@洛基斯塔里-实际上,这种技巧(试图把实现放进头)在java(或者Heck,甚至C++)之前就已经过时了。我见过代码使用类似的技巧在它的头文件中声明它的变量,但是为了更好的度量,在上面抛出了一个宏。如果需要在头文件中提供模板实现,那么可能只会导致一个头文件。你考虑过了吗?[至少通过一个头文件,您可以控制定义的顺序,避免定义宏给包含库的人员带来的负担]如果您不了解标记,请让我告诉您,重新考虑您的设计,如果您需要提供已编译的二进制文件,则并非每个库都是仅头文件(或供用户编译的源代码)不是标准中的每个库都是只头的,不是boost中的每个库都是只头的,而且不要认为你能比编写这种语言的人或boost中的人更聪明。哇,我本以为这会产生重复的符号,但它不是——这真的很好。我猜编译器仍然把它作为一种指示但是,实际上内联方法?查看
inline
关键字的一种方法是,它告诉编译器“一个定义规则”不适用于此函数。是的,编译器通常会尝试内联函数,但不一定要内联。我想同样的效果也可以实现(没有内联)