当不需要包含时

When are includes not needed?

本文关键字:包含时 不需要      更新时间:2023-10-16

我承认在包含方面我有点天真。我的理解是,如果你在类中使用它,你要么需要包含它,要么需要前向声明它。但是当我检查一些代码时,我看到了这个:

// 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.hC.h之前有#includehelper.h,并且你声明了这些类型的对象。

同样,依靠头文件的排列来让代码工作也不是一个好的做法,因为一旦你很少改变排列,整个层次结构就会崩溃,并产生几个编译错误。

代替#include在文件中的一切,如果你正在使用它,你可以使用#ifndef守卫来避免多个包含:

//helper.h
#ifndef HELPER_H
#define HELPER_H
// content of helper file
#endif

如果BC的类定义实际上没有引用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>的定义已经包含在使用这些构造函数的编译单元中。

相关文章: