Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/132.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 是否值得提前声明库类?_C++_Forward Declaration - Fatal编程技术网

C++ 是否值得提前声明库类?

C++ 是否值得提前声明库类?,c++,forward-declaration,C++,Forward Declaration,我刚刚开始学习Qt,使用他们的教程。我目前正在学习教程7,在那里我们制作了一个新的LCDRange类。LCDRange(.cpp文件)的实现使用Qt QSlider类,因此.cpp文件中的 #include <QSlider> 据Qt说, 这是另一个经典的技巧,但很少使用。因为我们在类的接口中不需要QSlider,所以只在实现中,我们在头文件中使用类的前向声明,并在.cpp文件中包含QSlider的头文件 这使得大型项目的编译速度大大加快,因为编译器通常花费大部分时间解析头文件,而

我刚刚开始学习Qt,使用他们的教程。我目前正在学习教程7,在那里我们制作了一个新的LCDRange类。LCDRange(.cpp文件)的实现使用Qt QSlider类,因此.cpp文件中的

#include <QSlider>
据Qt说,

这是另一个经典的技巧,但很少使用。因为我们在类的接口中不需要QSlider,所以只在实现中,我们在头文件中使用类的前向声明,并在.cpp文件中包含QSlider的头文件

这使得大型项目的编译速度大大加快,因为编译器通常花费大部分时间解析头文件,而不是实际的源代码。单凭这一技巧通常可以将编译速度提高两倍或两倍以上


这值得做吗?这似乎是有道理的,但还有一件事需要跟踪——我觉得将所有内容都包含在头文件中会简单得多。

是的,它确实有帮助。如果您担心编译时间的话,另一件要添加到曲目中的事情是预编译头


查找并

标准库对标准头
中的某些iostream类执行此操作。但是,它不是一种普遍适用的技术—请注意,对于其他标准库类型,没有这样的标题,并且它不应该(IMHO)成为设计类继承人档案的默认方法


尽管这似乎是程序员最喜欢的“优化”,但我怀疑,像大多数优化一样,很少有人在有或没有这样的声明的情况下对项目的构建进行了实际计时。我在这方面的有限实验表明,在现代编译器中使用预编译头是不必要的

当然。C/C++构建模型是。。。时代错误(最好说)。对于大型项目来说,这成为一个严重的PITA

正如Neil正确指出的那样,这应该而不是成为你的课堂设计的默认方法,除非你真的需要,否则不要刻意去做

打破循环包含引用是必须使用前向声明的一个原因

// a.h
#include "b.h"
struct A { B * a;  }

// b.h
#include "a.h"  // circlular include reference 
struct B { A * a;  }

// Solution: break circular reference by forward delcaration of B or A
减少重建时间-想象一下下面的代码

// foo.h
#include <qslider>
class Foo
{
   QSlider * someSlider;
}
现在,如果大多数.cpp需要引入bar.h,那么它们也会间接引入foo.h。因此,foo.h的每一次更改都会触发所有这些.cpp文件的构建(这些文件甚至可能不需要知道foo!)。如果bar.h使用Foo的转发声明,则对Foo.h的依赖关系仅限于bar.cpp:

// bar.h
class Foo;
class Bar 
{
   Foo * kungFoo;
   // ...
}

// bar.cpp
#include "bar.h"
#include "foo.h"
// ...
这是一种常见的模式。它的用途有两个方面:首先,它提供真正的接口/实现隔离,另一个是减少构建依赖性。实际上,我认为他们的效用是50:50

您需要在标题中引用,不能直接实例化依赖类型。这限制了可以应用转发声明的情况。如果您明确地这样做,那么使用实用程序类(例如)来实现这一点是很常见的

构建时间值得吗?,我想说。在最坏的情况下,构建时间随着项目中文件的数量呈多项式增长。其他技术——如更快的机器和并行构建——只能提供百分比增益

构建速度越快,开发人员测试他们所做的事情的频率就越高,单元测试运行的频率就越高,修复构建中断的速度就越快,开发人员拖延时间的频率也就越低


在实践中,管理构建时间虽然对大型项目(例如,数百个源文件)至关重要,但对小型项目来说仍然是“舒适的差别”。此外,在事后添加改进通常是一种耐心练习,因为一次修复可能只会缩短40分钟构建的秒数(或更少)

我一直在用它。我的规则是,如果它不需要头,那么我会提出一个前向声明(“如果必须使用头,如果可以使用前向声明”)。唯一糟糕的是我需要知道类是如何声明的(struct/class,如果它是一个模板,我可能需要它的参数,…)。但在绝大多数情况下,它只能归结为
类滑块;“
或类似的东西。如果需要声明一些更麻烦的内容,可以像标准对
iosfwd
一样声明一个特殊的前向声明头

不包含头文件不仅可以减少编译时间,而且可以避免污染名称空间。包含标题的文件将感谢您尽可能少地包含这些文件,以便它们可以继续使用干净的环境

这是一个粗略的计划:

/* --- --- --- Y.hpp */
class X;
class Y {
    X *x;
};

/* --- --- --- Y.cpp */
#include <x.hpp>
#include <y.hpp>

...
/*--Y.hpp*/
X类;
Y类{
X*X;
};
/*——Y.cpp*/
#包括
#包括
...
有些智能指针专门设计用于处理指向不完整类型的指针。一个非常著名的例子是。

一般来说,没有

我曾经尽可能多地转发声明,但现在不再

就Qt而言,您可能会注意到有一个
包含文件,该文件将拉入所有GUI小部件。此外,还有一个
等。每个模块都有一个头文件。Qt团队似乎也认为这是首选的方法。他们在模块文档中这样说


诚然,编译时间可能会增加。但根据我的经验,这并不多。如果是的话,下一步就是使用预编译头。

转发声明对于打破循环依赖关系非常有用,有时可以与您自己的代码一起使用,但与库代码一起使用可能会破坏另一个平台或库的其他版本上的程序(如果你不够小心,即使在你的代码中也会发生这种情况)。我认为这不值得。

对于大型应用程序,编译时间有很大的不同
// bar.h
class Foo;
class Bar 
{
   Foo * kungFoo;
   // ...
}

// bar.cpp
#include "bar.h"
#include "foo.h"
// ...
/* --- --- --- Y.hpp */
class X;
class Y {
    X *x;
};

/* --- --- --- Y.cpp */
#include <x.hpp>
#include <y.hpp>

...