不完整类型嵌套名称说明符 JUCE

Incomplete Type InNested Name Specifier JUCE

我在尝试使用 'WindowList' class 管理我的 windows 时遇到循环依赖问题。当我想用下面的 closeButtonPressed 代码关闭 window 时,我需要从 windowList 文件中删除该对象,但是我在 WindowList 文件中包含了 WindowSetter。以前的此类错误可以通过前向声明解决,但是我不确定如何解决这个错误。有什么建议吗? (完整代码可以在这里查看:https://gist.github.com/anonymous/7d43c6d5b2cf1fef618be9f75077ad0c

#pragma once

#include "../JuceLibraryCode/JuceHeader.h"
#include "WindowList.h"
class WindowList;

class WindowSetter  : public DialogWindow
{
public:
WindowSetter (const String& title,
             Component* content,
             bool shouldBeResizeable,
             int initWidth, int initHeight,
             int minWidth, int minHeight,
             int maxWidth, int maxHeight)
:   DialogWindow (title, Colours::white, true, true),
    owner (this)
{
    setUsingNativeTitleBar (true);
    setResizable (true, true);
    setResizeLimits (minWidth, minHeight, maxWidth, maxHeight);
    setContentOwned (content, false);

    setVisible (true);


}

~WindowSetter()
{
}

void closeButtonPressed() override
{
    WindowList::getWindowList();        // ERROR: Incomplete type 'WindowList' named in nested name specifier
    owner = nullptr;
}

bool escapeKeyPressed() override
{
    return true;
}

private:
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WindowSetter)

ScopedPointer<Component> owner;
};

编辑:添加了导致错误的文件的完整代码和错误日志截图

让我们从假想的编译器的角度看一下您的代码,看看到底发生了什么...

// NOTE: we're in WindowSetter.h

#include "WindowList.h"

没问题,让我们看一下该文件并将其包含在此处...

// NOTE: we're in WindowList.h

#include "WindowSetter.h"

好的,我们去看看...

// NOTE: we're back in WindowSetter.h

#include "WindowList.h"

但是……我就是这么做的。天哪,我陷入了无限循环。帮助!

KABOOM

编译器无法包含您要求的 header,因为它们在无限循环中相互需要。您需要以这种递归方式不包括 header 来打破这个循环。

您已经知道的前向声明对此很有用。原因是因为它们可以通知编译器有关类型名称的信息,而无需包含 header。如果编译器只需要知道类型名称,而不需要知道其他任何东西(例如大小),那就太好了。

你在这里遇到的问题,撇开体系结构问题,是你在这里 WindowSetter 内部使用 WindowList 的内部结构:

    WindowList::getWindowList();        // ERROR: Incomplete type 'WindowList' named in nested name specifier

这可能就是您最终将 header 放在首位的原因;如果你不这样做,它会给你一个类似的错误。

要解决此问题,您可以将成员函数 closeButtonPressed() 的定义移到 header 文件之外并移到 .cpp 文件中。为了保持一致性,您可能也想移动其他功能(个人意见问题)。

完成后,您将不再在 WindowSetter.h 中使用 WindowList 的详细信息,因此可以停止包含它。

这将解决您的问题,除非其他地方隐藏了其他循环依赖项(我没有全部阅读)。

OMGtechy 的回复解决了您提出的问题,但我想推荐一种不同的设计:

  1. 无需担心循环依赖
  2. 是更惯用的 JUCE 代码。

这里的设计将所有东西紧密地结合在一起。解决问题的更 JUCE-y 方法是使用 ChangeBroadcaster / ChangeListener 类 来消除紧密耦合。当您将 WindowSetter 添加到您的 WindowList 时,还要订阅其更改消息。当用户单击关闭按钮时,WindowSetter 设置一个布尔值并提醒任何收听它的人它已更新。

在草图中,它看起来像

class WindowSetter : public DialogWindow
                   , public ChangeBroadcaster

{
public:
   WindowSetter( /*(etc...)*/)
   : DialogWindow(...)
   , owner(this)
   , wantsToClose(false)
   {
      // etc

   }

   void closeButtonPressed() override
   {
      wantsToClose = true;
      // notify observers that we've changed. 
      sendChangeMessage();
   }

   bool windowWantsToClose() const 
   {
      return wantstoClose;
   }

private:
   bool wantsToClose;
};


class WindowList : public ChangeListener 
{

   void addWindowSetterToList(WindowSetter* wnd)
   {
      wnd->addChangeListener(this)
      windows.addIfNotAlreadyThere(wnd);
   }

   void changeListenerCallback(ChangeBroadcaster* src) override 
   {
      // cast from the ChangeBroadcaster base class to our WindowSetter class.
      WindowSetter* wnd = dynamic_cast<WindowSetter*>(src);
      if (nullptr != wnd)
      { 
         // if we contain the object, and the object wants to be closed...
         if (windows.contains(wnd) && wnd->windowWantsToClose())
         {
            // get rid of it. 
            windows.remove(wnd);
         }
      }
   }

};

你会看到这种设计在 JUCE 代码库中几乎无处不在。