【问题标题】:Forward declaration include, on top of declaration include (ClassFwd.h + Class.h)前向声明包括,在声明包括之上(ClassFwd.h + Class.h)
【发布时间】:2011-04-25 12:17:21
【问题描述】:

在 Effective C++(第 3 版)中,Scott Meyers 在第 31 条中建议类应该在其经典的声明 (.h) 和定义 (.cpp) 文件之上具有前向声明包含文件 (fwd. h),不需要完整定义的类可以使用,而不是前向声明自己。

我有点理解它的情况,但我真的不认为这是一个可行的选择......它似乎很难维护,相当矫枉过正而且几乎没有必要。

但是,我可以看到它在模板前向声明中的使用,这是相当繁重的。但是对于简单的课程?似乎维护起来很痛苦,并且会创建大量几乎为空的包含文件,这些文件的用途非常小……值得麻烦吗?

这是一个例子:

// Class.h
class Class
{
    Class();
    ~Class();
};

// ClassFwd.h
class Class;

// Class.cpp
Class::Class()
{
}

Class::~Class()
{
}

我的问题

你们怎么看?如果这是一个好习惯?

注意我最感兴趣的是这种做法的论点,看看我是否遗漏了一些能让我同意 Scott Meyers 的东西。

【问题讨论】:

    标签: c++ include forward-declaration effective-c++


    【解决方案1】:

    如果您有一个大型解决方案,这是您处理固有依赖关系的唯一机会:

    struct A {
        B* m_pB;
    };
    
    struct B {
        A* m_pA;
    };
    

    现在 A 和 B 可能合理地位于不同的头文件中,甚至可能位于不同的项目中。然而,它们的依赖并不是某种设计缺陷,而是完全合乎逻辑和必要的。你做什么工作?

    1. 首先为每个所需项目包含一个 Types.h 前向声明头,即,前向声明每个类没有自己的头文件。
    2. 然后包含所有必需项目的 Class.h。这些标头将需要前向声明才能编译。
    3. 包括主项目的标题。

    在一个相当大的解决方案(500k LOC)中,我发现这种模式非常易于管理。否则,如果您更改一个类声明,您在哪里可以找到您在任意数量的其他头文件中单独进行的所有前向声明?

    【讨论】:

    • 你的例子似乎是一般前向声明的例子,而不是 fwd.h 文件。
    【解决方案2】:

    在自己的标头中放置一个简单的class Whatever; 没有优点,反而有很多缺点。

    特别是在访问头文件可能很耗时的情况下,使用简单的前向声明来避免访问头文件;将它们放在自己的标题中会破坏目的......

    正如您所指出的,对于模板化的东西,这是另一回事。例如。从标准库中签出<iosfwd>

    干杯&hth。

    【讨论】:

    • 我的问题是我同意你,但不同意迈耶斯。可能是我正在寻找的答案,但我希望我的意见受到挑战! :D
    • 使用现代磁盘缓存,访问标头所造成的编译时间(和内存)并不取决于标头的数量,而是内部代码的复杂性。那么,包含一个更简单的标题来代替完整的定义并不会破坏目的。
    • @BenVoigt:关于“包含一个更简单的标题来代替完整定义”,问题是包含标题只写class X;我>。前者需要至少一个额外的磁盘访问。建立农场是有原因的。
    【解决方案3】:

    这种做法允许代码用户不必考虑类是常规类还是模板类。用户只需#inludes“corresponding_fwd.h”文件并具有类引用。对用户来说少一点烦恼是一件好事。但是,如果它是一个小项目或类的创建者是唯一的类用户,那么它可能会更烦人。所以,这取决于。

    【讨论】:

    • 好的,我明白你的意思了。考虑到额外的努力,我仍然有点怀疑它是否值得。看来,如果我是纯粹的用户,那还可以,但如果我也是开发人员,那么我不假思索地节省的时间或精力包括 fwd.h 都会被浪费在创建它们的时间所抵消......
    【解决方案4】:

    我对所有库都使用了前向声明头文件。库通常具有以下结构:

    lib/
      include/
        class headers + Fwd.h
    src/
      source files + internal headers
    

    lib/include 目录将包含所有公共类标头以及一个前向声明标头。这使得库在包含方面变得轻量级。此库之外的任何标头仅包含转发标头 (Fwd.h),而此库之外的源包含必要的完整标头。还可以提供一个方便的标头 (Lib.h),其中包含所有其他标头,以便在源文件中使用。

    在前向声明标头中放置的另一件事是typedefs for shared_ptr,尤其是在具有返回实现指针的工厂类的继承层次结构的情况下。

    以上内容对于具有大量内部库的大型应用程序很有用。针对这种情况,对上述内容的改进是将公共标头放在lib/include/lib 中。这样您图书馆的客户就必须包括lib/...。将其视为标题的命名空间。

    祝你好运!

    【讨论】:

    • 感谢您的回复,但是,您只是在说明您在做什么,而不是在说明您为什么这样做。您提供的唯一参数是 shared_ptr,我已经通过 FORWARD_SHARED() 宏解决了这个问题,该宏自动转发声明一个类及其指针。这是非常轻量级且易于阅读的...
    • 好的,原因是为了减少编译时间和集中前向声明。如果所有前向声明都在一个文件中,则在创建新类时很容易添加新声明。与指针类型定义相同。为什么编译时间减少应该是显而易见的。你能理解这个吗?当你的代码库变得更大时,保持头文件干净,除了前向声明将有所帮助。
    • 啊!所以你有一个 Fwd.h 文件!每个标题没有一个!现在我明白这有什么意义了...好吧,我会考虑一段时间...
    • 没错! :-) 当我还是一名 C++ 开发人员时,它对我来说非常有效。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-12-12
    相关资源
    最近更新 更多