【问题标题】:Circular Dependency with Inherited Template Class继承模板类的循环依赖
【发布时间】:2014-04-28 15:06:04
【问题描述】:

我遇到了循环依赖问题。基本上我有两个类,第一个是一个模板类,它使用了我第二个类的一些功能。第二个类继承自我的模板类。

下面是一个简化的结构:

// Foo.h
#pragma once
#include "Bar.h"

template <class T> class Foo
{
     public:
        void DoSomething();
};

template <class T> void Foo<T>::DoSomething()
{
     Bar::GetInstance()->LogEvent();
}


//Bar.h
#pragma once
#include "Foo.h"

class Bar : public Foo<Bar>
{
    public:
        static Bar* GetInstance();
        void LogEvent();
};


//Bar.cpp
#include "Bar.h"

Bar* Bar::GetInstance()
{
     // return instance of Bar singleton class
}

void Bar::LogEvent()
{
     // log some event in a file
}

现在的问题是,当我编译代码时,我在 bar.h 中收到以下错误

Bar.h() : error C2504: 'Foo' : base class undefined
Bar.h() : error C2143: syntax error : missing ',' before '<'

据我所知,这绝对是一个依赖问题。如果我从“DoSomething”中删除对“LogEvent”的调用,并从 Foo.h 中删除引用“Bar.h”,问题就会消失。

但这并不是真正的解决方案,因为 Foo 需要包含在 Bar 中的功能,相反,bar 继承自 Foo 并且需要包含对 Foo.h 的引用。

那么 - 我该如何解决这个问题?我浏览了有关循环引用的其他帖子,但我无法解决问题。

谢谢

【问题讨论】:

  • 您在类定义的末尾缺少;。
  • @Mankarse 感谢现场。它们是实际代码中的它们。我已经编辑了帖子。
  • 另一方面,DoSomething() 是否需要在 Foo() 中?如果它需要 Bar,为什么不把它放在 Bar 中呢?
  • 也许我错过了一些东西,但T::GetInstance() 会满足您的需求吗? (至少是你现在拥有的方式)?
  • @dlf 感谢您的 cmets。我也设法编译成简化的代码。所以我有点难过。 DoSomething() 确实需要在 Foo 中。

标签: c++ templates inheritance circular-dependency circular-reference


【解决方案1】:

根据您发布的代码,foo.h 不需要包含 bar.h,如果不包含,问题就会消失。

重要的是编译器在看到 Bar 之前先看到 Foo,如果 #include "bar.h" 位于 foo.h 的顶部,这可能不会发生(取决于 foo.h 和 bar.h 的顺序是 #include在消费模块中编辑)。

也就是说,如果一个基类需要假设一个特定的派生类并通过名称调用它,那么似乎可能存在设计问题(基类中的虚函数会起作用吗?)。但是我不能在没有看到全貌的情况下做出这样的判断。

基于下面的cmets,我想你可以通过添加第三个文件来解决眼前的问题:

// LogEvent.h

void LogEvent(...);

// LogEvent.cpp
#include "bar.h"

void LogEvent(...)
{
   Bar::GetInstance()->LogEvent(...);
}

然后修改foo.h如下:

// Foo.h
#pragma once
#include "LogEvent.h"

template <class T> class Foo
{
     public:
        void DoSomething();
};

template <class T> void Foo<T>::DoSomething()
{
   LogEvent(...);
}

除非还有其他问题,否则至少应该让你编译。

【讨论】:

  • 正如您之前提到的,我相信代码编译的原因是由于我简化它的方式。实际上,我还有其他从 Foo 继承的类-但只有 1 个 Foo 需要功能。 Foo 是一个封装了 Win32 线程的类,作为状态机类的基础。它是模板类的原因是因为我希望状态函数成为类的一部分,这意味着我需要指向成员函数的指针。 Bar 是一个事件记录类,它是一个状态机。因此 Foo 需要 Bar 的原因是启用 Foo 类中的故障记录。这可能并不理想。
  • 除了您展示的那个之外,Foo 是否还有其他对 Bar 的引用?或者更准确地说,LogEvent() 是它使用的唯一 Bar 函数吗?
  • 不,有一些引用,但它们都指向同一个函数,本质上是 LogEvent。 Foo 并不严格需要 bar 的功能来运行,它只使用 Bar 在数据库中记录错误。 Bar 是一个用于该目的的单例应用程序。
  • 感谢 dlf。我看到这可以作为临时解决方案发挥作用。我想我需要检查我的#includes,看看为什么我的示例代码有效而我的应用程序无效。起初我认为这是因为我的示例代码实际上并没有实例化 Bar 类,但我已经这样做了,它似乎工作正常。即使我从 foo.h 中删除 bar.h 它也可以工作,但我不明白,因为 foo 调用了 Bar::GetInstance()。
  • 这是因为 Foo 是一个模板类,所以即使方法是用 foo.h 编写的,它们的实现也会放在你创建特化的任何地方。只要 bar.h 在那里可见,代码就可以工作。
【解决方案2】:

你的问题是函数的定义:

template <class T> void Foo<T>::DoSomething()
{
     Bar::GetInstance()->LogEvent();
}

这里不需要写Bar类名。

使用:

template <class T> void Foo<T>::DoSomething()
{
     T::GetInstance()->LogEvent();
}

这样,当你写 Foo&lt;Bar&gt; Bar 将替换 T。

我使用您发布的代码测试了更改并且它有效。

编辑:

我找到了一个使用模板专业化的解决方案。

将此添加到您的Foo.h:

template <>
class Foo<Bar>
{
     public:
        void DoSomething()
        {
             Bar::GetInstance()->LogEvent();
        }
};

这解决了你所有的问题。使用模板特化,如果模板 Foo 使用 Bar 类型作为参数进行实例化,您将实例化上面定义的类,而不是原始的 Foo。

因此,您不必再担心其他类没有方法GetInstance。您可以使用这种方法来扩展您的代码,并根据需要使用尽可能多的 Foo 特化。

【讨论】:

  • 是的,我认为在这个特定的例子中是的 - 我可以用 T:: 替换 Bar:: 但是我不想这样做,因为我有其他从 Foo 继承的类没有实现GetInstance() 方法,或者实际上是 LogEvent 方法。在这种情况下,代码不会为其他类编译。
  • 所以,你的问题不在于代码,你的问题在于设计。
  • 答案已编辑,我认为这可能会解决您的问题。
  • 有趣的选项,但我认为这在我的情况下不会完全起作用,因为我仍然需要Foo 类的非专门实例才能调用Bar::GetInstance()-&gt;LogEvent。我认为我尝试这样做的方式是可能的,但是关于我的#includes 发生了一些事情。
猜你喜欢
  • 2014-12-31
  • 2022-01-16
  • 2016-07-17
  • 1970-01-01
  • 2014-08-28
  • 1970-01-01
  • 1970-01-01
  • 2018-09-07
  • 1970-01-01
相关资源
最近更新 更多