在头文件中大规模转发声明类有哪些风险?

时间:2017-12-13 20:26:47

标签: c++

对于类class1的每个指针class1,使用class1的前向声明而不是包含{{1}}头文件,我们应该考虑哪些风险?

我只能看到优势:头文件的大小会更小。

2 个答案:

答案 0 :(得分:13)

在班级提供者的控制之外的前向声明是有问题的!我正在使用代码库,其中使用了许多前向声明。事情最初是伟大的,前向声明的存在成为遗产:

  • 无法将类从一个名称空间移动到另一个名称空间。如果没有前向声明,原始命名空间中的名称可以成为别名(typedefusing alias)。
  • 不能将类转换为类模板的特化,例如,在推广成功的类时非常有用。
  • 类模板不能由用户转发声明,因为只有类模板的第一个声明可以提供默认参数。

假设前向声明是通过类提供者/实现者控制下的头提供的(例如,实现提供类似于<iosfwd>的东西),这些问题是不相关的,因为声明的中心位置可以改变。但是,让用户决定声明实体会成为遗留问题,从而导致成本高昂。

上述提供声明的方法似乎引起了一些混淆。我试着澄清一下。在我看来,实现的单元是组件(它基于John Lakos的组件符号)。组件定义一个或多个密切相关的类和/或功能。组件的实现由多个文件组成:

  1. 头文件声明所有相关实体,这些实体也定义在使用组件时必须定义的实体,即用户可访问的类,枚举等定义
  2. 头文件仅声明组件提供的相关实体(多个相关组件可以共享一个这样的头文件; <iosfwd>是跨多个组件共享的此类头的示例)。
  3. 定义所有实体[实际上是ODR使用]的实现文件,这些实体仅由上面的标题声明。
  4. 至少一个带有测试驱动程序的文件,用于测试该组件定义的所有实体。
  5. 组件的用户在某些上下文中只需要知道组件中的名称,其中包括仅声明标头。在任何情况下,用户都不会在组件中提供名称声明:组件中名称的所有声明都是组件的提供者/实现者的责任。

答案 1 :(得分:1)

  

我只能看到优势:头文件的大小会更小。

这不是重点。

假设您在头文件中有类声明,如

namespace MyNamespace {
    class Baz;
}

class Foo {
public:
    void bar(const MyNamespace::Baz & x);
};

和单独翻译单元中的定义

#include "Baz.hpp"

void Foo::bar(const MyNamespace::Baz & x) {
    // actually do something with Baz
}

并且相反,头文件中包含所有内容(并且在Baz.hpp将被更改时,将重新编译所有相关源)

#include "Baz.hpp"

class Foo {
public:
    void bar(const MyNamespace::Baz & x);
};

声明,第一个版本可能有助于更快地编译代码。

特别是如果你有自己的标题和类声明,并且如果其中任何一个可能被更改,你只需要在代码库中重新编译翻译单元,而不是每个包含类型相关标题的源文件。< / p>

请注意,前向声明只能用于引用和指针。此外,还不能使用标题内联代码来解除对转发类型成员的引用。