当不需要包含时
When are includes not needed?
我承认在包含方面我有点天真。我的理解是,如果你在类中使用它,你要么需要包含它,要么需要前向声明它。但是当我检查一些代码时,我看到了这个:
// file: A.cpp
#include "Helper.h"
#include "B.h"
#include "C.h"
#include "A.h"
// ...
// file: A.h
B b;
C c;
// ...
// file: B.h
Helper h;
// ...
// file: C.h
Helper h;
// ...
谁能给我解释一下为什么B和C不需要包括Helper?此外,这种方式组织包含的优点/缺点是什么?(除了打字明显减少之外)
谢谢。
当您将某些头文件(或其他)#include
文件转换为.cpp
文件时,该#include
语句简单地替换为头文件的内容。例如:
//header.h
int i;
float f;
// file.cpp
#include"header.h"
int main()
{}
经过预处理阶段后,file.cpp看起来像,
int i;
float f;
int main()
{}
可以在g++中使用g++ -E file.cpp > TEMP
看到这一点,它只显示预处理的文件。
在你现在的问题上下文中,你必须在B.h
和C.h
之前有#include
和helper.h
,并且你声明了这些类型的对象。
同样,依靠头文件的排列来让代码工作也不是一个好的做法,因为一旦你很少改变排列,整个层次结构就会崩溃,并产生几个编译错误。
代替#include
在文件中的一切,如果你正在使用它,你可以使用#ifndef
守卫来避免多个包含:
//helper.h
#ifndef HELPER_H
#define HELPER_H
// content of helper file
#endif
如果B
和C
的类定义实际上没有引用Helper
类的任何成员,那么编译器不需要在它们的头文件中看到Helper
类的完整定义。Helper
类的前向声明就足够了。
例如,如果B
类的定义只使用指向Helper
的指针或引用,那么建议使用前向引用:
class Helper;
class B {
// <SNIP>
Helper* helper;
// <SNIP>
void help(const Helper& helper);
// <SNIP>
};
如果B
类的定义使用Helper
类的实例(即:它需要知道Helper
实例的大小),或者引用Helper
类的定义(例如在模板函数中),那么您需要使Helper
类的完整定义可见(很可能包括定义Helper
类的头文件):
#include "helper.h"
class B {
// <SNIP>
Helper helper;
// <SNIP>
void help(Helper helper);
// <SNIP>
};
何时使用include和向前声明的规则相对简单:在可以的时候使用向前声明,而在必须使用时使用include。
这样做的好处很明显:包含的内容越少,头文件之间的依赖关系就越少(编译速度就越快)。
把#include看作是在这个文件中包含另一个文件的文本—这与您复制它完全相同。因此,在这种情况下,B和C不需要包含Helper的原因是因为您已经将其包含在同一个"编译单元"中,这就是.cpp文件及其所有包含的组合。
对于模板,即使在头文件中引用了前向声明类型的成员,也可以使用前向声明。
例如,boost::shared_ptr<T>
实现只向前声明boost::weak_ptr<T>
,即使它在两个构造函数中使用。下面是从http://www.boost.org/doc/libs/1_47_0/boost/smart_ptr/shared_ptr.hpp:
namespace boost
{
// ...
template<class T> class weak_ptr;
// ...
template<class T> class shared_ptr
{
// ...
public:
// ...
template<class Y>
explicit shared_ptr(weak_ptr<Y> const & r): pn(r.pn) // may throw
{
// it is now safe to copy r.px, as pn(r.pn) did not throw
px = r.px;
}
template<class Y>
shared_ptr( weak_ptr<Y> const & r, boost::detail::sp_nothrow_tag ): px( 0 ), pn( r.pn, boost::detail::sp_nothrow_tag() ) // never throws
{
if( !pn.empty() )
{
px = r.px;
}
}
在这种情况下,boost::weak_ptr<T>
的前向声明就足够了,因为两个构造函数不会被实例化,除非前向声明的boost::weak_ptr<T>
的定义已经包含在使用这些构造函数的编译单元中。
- 为什么在调用C#DLL时不需要提供字符串缓冲区
- 为什么使用指向函数的指针调用虚函数时不需要指针"this"?
- 为什么我们在传递动态 2D 数组时不需要列数?
- 为什么在开始绘制获取的交换链图像时不需要内存障碍?
- 程序告诉我初始化在编写简单的C ++代码时不需要初始化的变量
- 我可以解决unique_ptr在<MyType>仅存储 nullptr 时不需要 MyType 析构函数定义吗?
- 为什么在"cin"后使用 "getline" 时需要 cin.ignore(),而多次使用 "cin" 时不需要?
- r语言 - 为什么当我使用 Rcpp 编写时,我不需要包含一些我应该以普通C++编写的库
- 在成员函数上启动线程时不需要的析构函数调用
- 为什么使用 GLFW 时不需要加载着色器?
- 为什么使用boost::function调用函数对象时不需要类实例
- 在c++ 11中,是否有一种方法可以在调用以任何可调用对象(包括绑定方法)作为参数的函数时不需要模板参数
- 为什么当我从向导在Visual Studio 2013中创建c ++类时,我不需要包含来自stdafx.h的标头
- 为什么 setjmp 在包含 时不在 std 命名空间中?<csetjmp>
- 为什么C++变量是指针时不需要正确定义?
- c++调用约定是否受标准约束,因为在声明fn时不需要定义函数的返回类型
- 为什么跨多个文件共享时不需要用"extern"装饰"const char *"
- 函数在作为 GLUT 项目的一部分包含时不读取文件
- 为什么在 c++ 中重载运算符时不需要范围解析 (::)?
- 使用 isalpha 函数时不需要 std 命名空间