如何避免涉及全局变量的循环 #include?

How to avoid a circular #include involving a global variable?

本文关键字:循环 #include 全局变量 何避免      更新时间:2023-10-16

我有一个声明两个全局对象的标头myglobal.h

#include "log.h"  // defines Clog
Clog log_file;
#include "lib.h"
Clib main_lib;

但是,我的lib.h也使用全局标头:

#include "myglobal.h"  // error: circular #include
class Clib
{
void func(void) { log_file << "hello"; }
}

如何编写这些标头以避免#include循环?

在头文件中添加extern

// myglobal.h
#include "log.h"
extern Clog log_file;
// #include "lib.h"  DO NOT include this one
extern Clib main_lib;

然后在 cpp 文件 (log.cpp) 中定义它

#include "log.h"
Clog log_file;

并将 clib 的定义也move到 cpp 文件(例如 lib.cpp)。

#include "myglobal.h"
void Clib::func(void) { log_file << "hello"; }

如果无法将定义移出头文件,则只需要一个 cpp 文件来定义Clib main_lib。除此之外,只要您包含对所有标头的防护,它应该没问题。

循环 #include 的根本问题是循环思维。特别是,全局标头的概念既定义又依赖于您的库。

您可以通过依赖于库实现中的myglobal.h来解决此问题。一种简单的方法是将CLib::func移动到lib.cpp

首先,如 CS Pei 的回答中所述,请注意您需要包含守卫,并且头文件中的变量声明需要extern(以及源文件中的单独定义)。 在 C++17 中,您可以改用inline,并让链接器对其进行排序。

同时,根据您的用例,有一些策略可供选择。

前向声明Clib

myglobal.h只声明Clib,而不是定义它:

#include "log.h"
extern Clog log_file;
class Clib;
extern Clib main_lib;

然后可以毫无问题地#includelib.h。 但是,单独#include"myglobal.h"的客户端将无法使用main_lib,因为它的类型不完整。

log_file放在其自己的标头中

由于Clib不仅依赖于Clog,还依赖于log_file,因此它显然处于更高的接口级别。 通过创建globallog.h将其形式化:

#include "log.h"
extern Clog log_file;

然后lib.h可以包括没有循环性的内容:

#include "globallog.h"
class Clib
{
void func(void) { log_file << "hello"; }
}

最后,myglobal.h包括这一点并添加main_lib

#include "globallog.h"  // optional: clarifies that log_file is made available
#include "lib.h"
extern Clib main_lib;

声明全局对象及其类

lib.h中声明main_lib

如果没有文件想要没有(您当前的)myglobal.hlib.h(可能是因为唯一使用的Clibmain_lib),您可以将main_lib声明移动到lib.h

#include "myglobal.h"  // only log_file now
class Clib
{
void func(void) { log_file << "hello"; }
}
extern Clib main_lib;

请注意,这是之前"log_file在其自己的标头中"策略的变体,其中log_file是现有标头中唯一保留的内容。

log.h中声明log_file

对称地,如果没有文件想要log.h而不使用log_file,请在此处声明它并只留下myglobal.hmain_lib

#include "lib.h"
extern Clib main_lib;

那么lib.h只是

#include "log.h"
class Clib
{
void func(void) { log_file << "hello"; }
}