"source-less" C++成语

A "source-less" C++ idiom

本文关键字:成语 source-less C++      更新时间:2023-10-16

我正在开发一个相当大的C++支持库,并发现自己正朝着只使用头的方法发展。在C++中,这几乎是有效的,因为您可以在类中定义的地方实现。对于模板化方法,实现无论如何都必须在同一个文件中,所以我发现只保留具有定义的实现要容易得多。

然而,有时必须使用"来源"。仅举一个例子,有时会出现循环依赖关系,并且必须在类定义之外编写实现。以下是我的处理方式:

//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::CircularDependency()的定义显示在libfoo.h标头中时得到解决,则应该能够使用inline关键字:

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

// other stuff that 'resolves' the circular dependency
// ...
// ...
inline void Bar::CircularDependency(void)
{
  //...
}

这将使您的库更易于使用(用户不需要处理LIBFOO_COMPILE_INLINE需要在包含头的一个地方定义的事实)。

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

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

编译时间增加

包括头的每个单独的编译单元除了本身的源文件外,还应该编译所有的头文件。这可能会增加编译时间,并且在测试代码时可能会令人沮丧,只需进行一些小的更改。有人可能会说编译器可能会对其进行优化,但在IMO中,它无法对其进行超出某一点的优化。

更大的代码段

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

在具有紧密耦合的客户端代码中创建依赖性

每当您更改实现时,每个客户端都应该得到更新(通过重新编译代码)。但是,如果实现被放在一个独立的共享对象(.so或.dll)中,那么客户端应该只链接到新的共享对象。

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

//main.cpp
#define LIBFOO_COMPILE_INLINE
#include "libfoo.h"

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

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