首页 > 解决方案 > 如何避免仅标头库中的循环依赖?

问题描述

我正在开发一个 C++ 标头库。

有几个部分的代码遵循这种模式:

持有人.h

#pragma once

#include "sub.h"

struct Holder
{
    void f();
    Sub s;
};

子.h

#pragma once

struct Holder;

struct Sub
{
   void g(Holder& h);
};

#include "sub.ipp"

子.ipp

#include "holder.h"

inline void Sub::g(Holder& h)
{
    h.f();
}

sub.h使用前向声明避免了对Holder的循环依赖。但是,在holder.h中,由于Holder类包含一个Sub成员,它需要在sub.h中查看Sub的完整声明。但是sub.h 引入了sub.ipp中的实现,它还不能被实例化,因为它需要Holder的定义并且我们已经在holder.h 中,所以我们不能再次包含它。

当我使用这些头文件中的任何一个时,我只想包含正确的.h文件,而不必担心在陌生的地方手动包含正确的.ipp文件。

对此的标准解决方案是什么?

标签: c++header-files

解决方案


struct Sub
{
   void g(Holder& h);
};

void Sub::g(Holder& h)
{
    h.f();
}

非内联函数在只有标头的库中不能很好地工作,因为标头通常包含在多个翻译单元中。您应该改用内联函数。


如何避免仅标头库中的循环依赖?

您必须将函数的定义与类的定义分开。我的意思是,它们已经在单独的文件中,但是定义类的标头不能包含函数定义。这允许打破依赖循环。

这可能是个人喜好问题,但我也不喜欢不能独立工作的“ipp”标头。

例子:

细节/holder_class_only.h

#pragma once
#include "detail/sub_class_only.h"
struct Holder
{
    inline void f(); // note inline
    Sub s;
};

详细信息/sub_class_only.h

#pragma once
struct Holder;
struct Sub
{
   inline void g(Holder& h); // note inline
};

细节/holder_functions.h

#pragma once
#include "detail/holder_class_only.h"
void Holder::f()
{
}
#include "detail/sub_functions.h"

细节/sub_functions.h

#pragma once
#include "detail/sub_class_only.h"
#include "holder.h"
void Sub::g(Holder& h)
{
    h.f();
}

子.h

#pragma once
#include "detail/sub_class_only.h"
#include "detail/sub_functions.h"

持有人.h

#pragma once
#include "detail/holder_class_only.h"
#include "detail/holder_functions.h"

注意:未经测试,可能包含错误。


推荐阅读