Search code examples
c++qtqt-creator

I encapsulated functionality in a class, but now the class can't use functions I need from the class that is using it


I'm making a Qt application that is a variant of TicTacToe in which multiple boards can be created. In the window class for the game where the boards are (PlayGameWindow), I had a lot of functions handling the boards and was passing the same 3+ variables between them. It made sense to move them all in a Board class. The Board class has a QGridLayout as the board and labels for the symbol spaces (3x3 labels for example).

I have a click listener for the (modified) QLabels so when a user presses a space, it does some work with the board. During the event, I also need to work with the players. (The function used by the listener is the only place in the Board class in which I need to call functions of PlayGameWindow.) For example, I need to iterate to the next player, or get the current player symbol to put into the space.

I'm not sure which code to share to help with this post. Here are the two functions related to the board space click listener. In this version, spaceClicked is a friend function, and addClickedSpace is using gameW, a class variable set by PlayGameWindow.

void Board::addClickedSpace(BoardSpaceLabel* space)
{
    QObject::connect(space, &BoardSpaceLabel::clicked, this,
        [this, space]() { spaceClicked(space, this, gameW); }
    );
}

void spaceClicked(BoardSpaceLabel* space, Board* b, PlayGameWindow* w)
{
    // if space has text or image
    if (space->getSymbol() != "")  return;

    space->setSymbol(w->getCurrPlayerSymbol());

    QVector<QVector<BoardSpaceLabel*>> wins = b->getWinSpaces(space);

    b->displayWins(wins);

    if (0 < wins.count() || b->boardIsFull())  b->disableBoard();

    w->addCurrPlayerScore(wins.count());

    w->iteratePlayer();

    w->highlightCurrPlayer();
}

I have tried passing the instance of PlayGameWindow into each Board instance so that the Board class may use the PlayGameWindow functions, but while the debugger goes to the correct PlayGameWindow function, it segfaults when it tries to use any of the PlayGameWindow variables or objects, and the PlayGameWindow instance information cannot be viewed in the debugger.

I have tried making either the PlayGameWindow functions or the function used by the click event a friend, but using the PlayGameWindow functions results in the same problem.

I have tried sending the functions as lambda functions to later call in the click event function, but that brings the same problem. I can't use a PlayGameWindow function in the Board class, even if it's public.

My last resort is to no longer encapsulate the Board class and put everything back in the PlayGameWindow class, but I'm hoping to avoid this because the board functionality is benefiting from encapsulation.


Solution

  • From your description, it seems that you are having trouble in defining relationship between PlayGameWindow and Board class. Difficulty in accessing a class object, indicating towards a design issue.

    In a typical Qt application, widgets often follow a parent/child relationship. In this case, it would be appropriate to make PlayGameWindow the parent class and Board the child class. Additionally, the labels should be children of the board. This hierarchical relationship aligns with the expected structure of the application.

    To access the parent class (PlayGameWindow) from the child class (Board), it's recommended to use dependency injection rather than relying on aggregation (making the parent a member variable of the child class). Dependency injection involves passing the parent object as a parameter to the child class constructor or a specific method, ensuring that the child class has a reference to its parent.

    you should not use parent (PlayGameWindow) as a class member to child (Board).

    For example

    #include <QObject>
    #include <QWidget>
    #include <QGridLayout>
    #include <QPushButton>
    
    // Forward declaration of Board class
    class Board;
    
    class GameWindow : public QWidget {
        Q_OBJECT
    public:
        explicit GameWindow(QWidget* parent = nullptr);
        ~GameWindow();
    
    private slots:
        void createBoard();
    
    private:
        QGridLayout* layout;
        QPushButton* addButton;
        QList<Board*> boards;
    };
    
    class Board : public QWidget {
        Q_OBJECT
    public:
        explicit Board(GameWindow* parent = nullptr);
        ~Board();
    
    private slots:
        void spaceClicked();
    
    private:
        QGridLayout* layout;
        // Other member variables and functions related to the board
    };
    

    The Board class has a GameWindow* pointer as its parent. This establishes the parent-child relationship, where the GameWindow is the parent and the Board is the child. The parent-child relationship is useful for managing memory and ensuring proper cleanup when the parent is deleted.

    Note: The provided code is a simplified example to demonstrate the parent-child relationship between GameWindow and Board classes. You may need to modify and expand the code to fit your specific application requirements.