我想知道在可能的情况下在所有地方使用前向声明是否有任何缺点。这是我的标题只包含声明。
据我了解,使用前向声明可加快编译时间,但我不知道有任何缺点。
A.H:
Class A
{
};
b.h:
// Should I use and include "a.h" in the cpp file (e.g., a.cpp)
Class A;
Class B
{
doSomething(A *a);
A *myA;
};
或者使用
更好b.h:
#include "a.h"
Class B
{
doSomething(A *a);
A *myA;
};
答案 0 :(得分:10)
使用前向声明可以改善解耦。如果您可以通过使用前向声明来避免包含"A.h"
,那么使用前向声明是个好主意。最好不仅因为你的构建运行得更快(毕竟,预处理的头文件可以很好地处理编译器的效率),但是因为它告诉读者你的声明你的类B
的结构不依赖于知道任何东西关于你的班级A
,除了它存在 * 。
编辑(回答你的问题)我知道转发声明的唯一缺点就是你无法在所有情况下使用它们:例如,类似于此的声明:
class B
{
A myA[10];
};
无法编译,因为编译器需要知道A
的大小。但是,编译器可以非常可靠地发现这些问题,并以明确的术语通知您。
* 类B
的实现很可能取决于了解类A
的详细信息。但是,此依赖关系成为对您的类用户隐藏的B
的实现细节;您可以随时更改它,而不会破坏依赖于类B
的代码。
答案 1 :(得分:2)
使用前向声明可加快编译时间
这是部分正确的,因为编译器(和预处理器)不需要在包含此标头的每个文件中解析包含的标头。
更改标题并需要重新编译时看到的真正改进。
前向声明是打破循环包含的唯一方法。
答案 2 :(得分:1)
我会用实际的话说。优点:
避免循环编译器依赖项。除非你把A和B放在同一个标题中,否则你编写上面代码的方式甚至都不会编译。
它避免了编译时依赖性。您可以在不重新编译包含b.h的单元的情况下更改a.h.出于同样的原因,它通常可以加快构建速度。要了解有关此主题的更多信息,我建议查找Pimpl习语。
缺点:
以上述方式大量应用,您的一般源文件可能需要包含更多标题(我们无法通过包含B.h来实例化或使用A)。对我而言,这对于更快的构建来说是一次有价值的交流。
这可能是最大的问题,它可能会带来一些运行时开销,具体取决于您正在做什么。在您给出的示例中,B不能直接将A存储为值。它涉及一个间接级别,如果B是A的内存管理器(对于pimpl也是如此),这也可能意味着额外的堆分配/释放。这种开销是否微不足道是您必须绘制线条的地方,值得记住的是,可维护性和开发人员的工作效率肯定比微观优化更重要,而微优化对用户来说甚至都不会引起注意。我不会用这个作为排除这种做法的理由,除非它肯定被证明是瓶颈或你事先知道堆分配/释放或指针间接的成本将是一个非平凡的开销
答案 3 :(得分:1)
转发声明是打破循环包含的唯一方法。
我认为,如果不仔细使用,这是主要缺点。我在一个大型项目中工作,在任何可能的情况下都进行前向声明。循环依赖最终是一个真正的问题。
答案 4 :(得分:0)
想到的唯一缺点是前向声明需要指针。因此,它们可能未初始化,因此可能导致空引用异常。作为我目前使用的编码标准,要求所有指针都需要空引用检查是否可以添加分配代码。我开始用Design By Contract不变量来解决这个问题。然后我可以断言在构造函数中初始化的任何东西都不会为null。