0

so my problem isn't as much about the code, but about the way to do it. I'm working on a GUI and I want my buttons to know who there parent is. And of course, the window knows which buttons it has.

This creates a circular dependency, since both need to be able to access the others methods and attributes, at least that's what I would prefer.

I found a solution that works, but I'm not very happy with it:

I created a third object to which button writes, what it wants the window to do. And the window checks this third object for commands.

I wanted to ask you, if you know of a better way to this, since I couldn't find any other way, that works for me.

Thanks!

  • Why you want avoid circular dependences? – GingerPlusPlus Sep 18 '14 at 17:24
  • @GingerPlusPlus Because circular dependencies are a sign of bad and broken design. – Captain Obvlious Sep 18 '14 at 17:29
  • @GingerPlusPlus because you cannot compile code that has circular dependencies, and if you have them in the first place your code is not architectured correctly. – Cory Kramer Sep 18 '14 at 17:29
  • Since this is a program I write for getting familiar with c++, I go with the answer from cyber: Because my compiler doesn't let me compile it. – LordOfThunder123 Sep 18 '14 at 17:32
  • Try reference the objects as pointers, I know there's a difference in how circular dependencies work using pointers and don't. But I don't have a reference here =/ – MVCDS Sep 18 '14 at 17:43
  • Being able to speak directly to each other is not, of itself, a problem. Just be sure which is owner and which has a non-owning back-reference (model with `std::unique_ptr` and a raw pointer). Alternatively, if you have shared ownership, one gets a `std::shared_ptr` and the other a `std::weak_ptr`. – Deduplicator Sep 18 '14 at 17:46
  • @MVCDS: Tried that already. It would work if I didn't need access to the functions and variables of the pointers. But I can't forward declare a class with its members. – LordOfThunder123 Sep 18 '14 at 17:46
  • You know, you can forward-declare the class, which is enough for defining pointer-members (though not `unique_ptr`), and if you implement as needed outside the class, you can avoid the catch-22. – Deduplicator Sep 18 '14 at 17:49
  • Why you are not happy the solution you found? See this question: http://stackoverflow.com/questions/1897537/why-are-circular-references-considered-harmful – Willem Sep 18 '14 at 17:50
  • @Deduplicator: The window holds the buttons. The buttons only need to be able to e.g. change the window title. But why wouldn't that create the same problem when trying to specify the type of the unique pointer. Or am I missing something? – LordOfThunder123 Sep 18 '14 at 17:50

2 Answers2

1

I'd suggest create a window interface. Provide a back pointer to the window interface in the constructor of the button. The window that owns the button depends on the button and the button depends on the window interface. No circular dependency.

struct IWindow {
};

struct Button {
 IWindow* window_;
 Button(IWindow* window) : window_(window){}
};

struct WindowWithButton : IWindow {
  Button button_;
  WindowWithButton() : button_(this) {}
};

Then add virtual methods to IWindow that are implemented by WindowWithButton so that Button can get the information it needs from the WindowWithButton.

Chris Drew
  • 14,926
  • 3
  • 34
  • 54
0

It's a standard pattern:

struct Window; // Forward-declare the parent
struct Button {
    void pingParent(); // Only declare members which need
        // more than superficial knowledge of Window
    Window* parent; // Ok, we know there is a Window, somewhere
};
struct Window {
    unique_ptr<Button> child;
    // Other functions using the button
    void pingpong() {child->pingParent();}
    void ping(){}
};
/*optional inline*/ void Button::pingParent() {
    parent->ping();
}
Deduplicator
  • 44,692
  • 7
  • 66
  • 118
  • Thanks to you too. Your solution would work as well. But when I'm working with includes, wouldn't I need to define the actual pingParent() in the Window.h/Window.cpp? Doesn't this make the code quite confusing? – LordOfThunder123 Sep 18 '14 at 18:16
  • You define it in a header so every TU calling it has the definition. If you don't use `inline`, it must be in exactly one implementation-file, not in a header. I would avoid interfaces (with virtual functions for overriding) unless needed, because there's a performance and space-penalty associated with using them. – Deduplicator Sep 18 '14 at 18:22