forward declarations give errors + I want some explanation



  • Hi I am using the find example from "C++ Gui programming with Qt" book chapter 2.
    There is the file finddialog.h :

    #ifndef FINDDIALOG_H
    #define FINDDIALOG_H
    
    #include <QDialog>
    
    class QCheckBox;
    class QLabel;
    class QLineEdit;
    class QPushButton;
    
    class FindDialog : public QDialog
    {
        Q_OBJECT
    
    public:
        FindDialog(QWidget *parent = 0);
    
    signals:
        void findNext(const QString &str, Qt::CaseSensitivity cs);
        void findPrevious(const QString &str, Qt::CaseSensitivity cs);
    
    private slots:
        void findClicked();
        void enableFindButton(const QString &text);
    
    private:
        QLabel *label;
        QLineEdit *lineEdit;
        QCheckBox *caseCheckBox;
        QCheckBox *backwardCheckBox;
        QPushButton *findButton;
        QPushButton *closeButton;
    };
    
    #endif
    
    

    I get errors with the class forward declarations at beginning of code.
    http://s13.postimg.org/jilgxzcsn/1_20_2016a.jpg

    I have included in project file the line

    QT      += widgets
    

    finddialog.cpp:

    #include <QtGui>
    
    #include "finddialog.h"
    
    FindDialog::FindDialog(QWidget *parent)
        : QDialog(parent)
    {
        label = new QLabel(tr("Find &what:"));
        lineEdit = new QLineEdit;
        label->setBuddy(lineEdit);
    
        caseCheckBox = new QCheckBox(tr("Match &case"));
        backwardCheckBox = new QCheckBox(tr("Search &backward"));
    
        findButton = new QPushButton(tr("&Find"));
        findButton->setDefault(true);
        findButton->setEnabled(false);
    
        closeButton = new QPushButton(tr("Close"));
    
        connect(lineEdit, SIGNAL(textChanged(const QString &)),
                this, SLOT(enableFindButton(const QString &)));
        connect(findButton, SIGNAL(clicked()),
                this, SLOT(findClicked()));
        connect(closeButton, SIGNAL(clicked()),
                this, SLOT(close()));
    
        QHBoxLayout *topLeftLayout = new QHBoxLayout;
        topLeftLayout->addWidget(label);
        topLeftLayout->addWidget(lineEdit);
    
        QVBoxLayout *leftLayout = new QVBoxLayout;
        leftLayout->addLayout(topLeftLayout);
        leftLayout->addWidget(caseCheckBox);
        leftLayout->addWidget(backwardCheckBox);
    
        QVBoxLayout *rightLayout = new QVBoxLayout;
        rightLayout->addWidget(findButton);
        rightLayout->addWidget(closeButton);
        rightLayout->addStretch();
    
        QHBoxLayout *mainLayout = new QHBoxLayout;
        mainLayout->addLayout(leftLayout);
        mainLayout->addLayout(rightLayout);
        setLayout(mainLayout);
    
        setWindowTitle(tr("Find"));
        setFixedHeight(sizeHint().height());
    }
    
    void FindDialog::findClicked()
    {
        QString text = lineEdit->text();
        Qt::CaseSensitivity cs =
                caseCheckBox->isChecked() ? Qt::CaseSensitive
                                          : Qt::CaseInsensitive;
        if (backwardCheckBox->isChecked()) {
            emit findPrevious(text, cs);
        } else {
            emit findNext(text, cs);
        }
    }
    
    void FindDialog::enableFindButton(const QString &text)
    {
        findButton->setEnabled(!text.isEmpty());
    }
    
    

    main.cpp:

    #include <QApplication>
    
    #include "finddialog.h"
    
    int main(int argc, char *argv[])
    {
        QApplication app(argc, argv);
        FindDialog *dialog = new FindDialog;
        dialog->show();
        return app.exec();
    }
    
    

    find.pro:

    QT      += widgets
    
    TEMPLATE      = app
    HEADERS       = finddialog.h
    SOURCES       = finddialog.cpp \
                    main.cpp
    

  • Qt Champions 2016

    hi
    when you say
    class QLabel;
    its ok to use it for pointers.

    QLabel *myname;

    But if you start to use ANYTHING in it
    like
    myname->setBuddy

    then a forward is NOT enough and u must include the include file for it
    #include <QLabel>

    So that is what incomplete class error means.

    as you only tell it the name. and if u use more than name, you must give compiler the full class definition.



  • In addition to what @mrjj said, it's a good practice to use forward declaration in your .h file, but you have to add #include <QLabel> in your .cpp for the reasons mrjj gave you. Indeed, the compiler is complaining about your .cpp file, not your .h file.

    But for instance, if you don't use a pointer for your variable, or if you inherit from an other class, then you have to #include it, since the compiler needs to know extra information about the content of the class.



  • @mrjj
    I added the #includes at finddialog.h:

    #ifndef FINDDIALOG_H
    #define FINDDIALOG_H
    
    #include <QDialog>
    #include <QCheckBox>
    #include <QLabel>
    #include <QLineEdit>
    #include <QPushButton>
    
    class QCheckBox;
    class QLabel;
    class QLineEdit;
    class QPushButton;
    
    class FindDialog : public QDialog
    {
        Q_OBJECT
    
    public:
        FindDialog(QWidget *parent = 0);
    
    signals:
        void findNext(const QString &str, Qt::CaseSensitivity cs);
        void findPrevious(const QString &str, Qt::CaseSensitivity cs);
    
    private slots:
        void findClicked();
        void enableFindButton(const QString &text);
    
    private:
        QLabel *label;
        QLineEdit *lineEdit;
        QCheckBox *caseCheckBox;
        QCheckBox *backwardCheckBox;
        QPushButton *findButton;
        QPushButton *closeButton;
    };
    
    #endif
    
    

    Do I need the forward declarations anymore?

    Also I get the errors:
    http://s11.postimg.org/4izv8mff7/1_20_2016b.jpg
    which I suspect that I haven't included the include files for QHBoxLayout and QVBoxLayout.
    In which file do I include these?



  • @SweetOrange

    Look at the file on the right of the error message, you'll know where to #include what the compiler lacks.


  • Qt Champions 2016

    Hi in 99% of all cases the include has same name as class.
    #include <QVBoxLayout>
    and no, no need for forward when u use the real include.



  • Maybe it's a bit technical, but here is a link on Stack Overflow explaining what a forward declaration is:
    http://stackoverflow.com/questions/553682/when-can-i-use-a-forward-declaration

    Basically, a forward declaration is only a "Don't worry compiler, this class exists." statement. As soon as the implementation of the class is required, the #include is mendatory.

    The question of "should I use #include or forward declaration here?" is off topic tho.



  • Thanks for the info.

    #ifndef FINDDIALOG_H
    #define FINDDIALOG_H
    
    #include <QDialog>
    #include <QLabel>
    #include <QLineEdit>
    #include <QCheckBox>
    #include <QPushButton>
    
    //class QCheckBox;
    //class QLabel;
    //class QLineEdit;
    //class QPushButton;
    
    class FindDialog : public QDialog
    {
        Q_OBJECT
    
    public:
        FindDialog(QWidget *parent = 0);
    
    signals:
        void findNext(const QString &str, Qt::CaseSensitivity cs);
        void findPrevious(const QString &str, Qt::CaseSensitivity cs);
    
    private slots:
        void findClicked();
        void enableFindButton(const QString &text);
    
    private:
        QLabel *label;
        QLineEdit *lineEdit;
        QCheckBox *caseCheckBox;
        QCheckBox *backwardCheckBox;
        QPushButton *findButton;
        QPushButton *closeButton;
    };
    
    #endif
    
    
    #include <QtGui>
    
    #include "finddialog.h"
    #include <QHBoxLayout>
    #include <QVBoxLayout>
    
    
    
    
    
    
    FindDialog::FindDialog(QWidget *parent)
        : QDialog(parent)
    {
        label = new QLabel(tr("Find &what:"));
        lineEdit = new QLineEdit;
        label->setBuddy(lineEdit);
    
        caseCheckBox = new QCheckBox(tr("Match &case"));
        backwardCheckBox = new QCheckBox(tr("Search &backward"));
    
        findButton = new QPushButton(tr("&Find"));
        findButton->setDefault(true);
        findButton->setEnabled(false);
    
        closeButton = new QPushButton(tr("Close"));
    
        connect(lineEdit, SIGNAL(textChanged(const QString &)),
                this, SLOT(enableFindButton(const QString &)));
        connect(findButton, SIGNAL(clicked()),
                this, SLOT(findClicked()));
        connect(closeButton, SIGNAL(clicked()),
                this, SLOT(close()));
    
        QHBoxLayout *topLeftLayout = new QHBoxLayout;
        topLeftLayout->addWidget(label);
        topLeftLayout->addWidget(lineEdit);
    
        QVBoxLayout *leftLayout = new QVBoxLayout;
        leftLayout->addLayout(topLeftLayout);
        leftLayout->addWidget(caseCheckBox);
        leftLayout->addWidget(backwardCheckBox);
    
        QVBoxLayout *rightLayout = new QVBoxLayout;
        rightLayout->addWidget(findButton);
        rightLayout->addWidget(closeButton);
        rightLayout->addStretch();
    
        QHBoxLayout *mainLayout = new QHBoxLayout;
        mainLayout->addLayout(leftLayout);
        mainLayout->addLayout(rightLayout);
        setLayout(mainLayout);
    
        setWindowTitle(tr("Find"));
        setFixedHeight(sizeHint().height());
    }
    
    void FindDialog::findClicked()
    {
        QString text = lineEdit->text();
        Qt::CaseSensitivity cs =
                caseCheckBox->isChecked() ? Qt::CaseSensitive
                                          : Qt::CaseInsensitive;
        if (backwardCheckBox->isChecked()) {
            emit findPrevious(text, cs);
        } else {
            emit findNext(text, cs);
        }
    }
    
    void FindDialog::enableFindButton(const QString &text)
    {
        findButton->setEnabled(!text.isEmpty());
    }
    
    
    #include <QApplication>
    
    #include "finddialog.h"
    
    int main(int argc, char *argv[])
    {
        QApplication app(argc, argv);
        FindDialog *dialog = new FindDialog;
        dialog->show();
        return app.exec();
    }
    

    Now works and compiles properly and runs.

    Some explanations though that I found the book lacks. What does the header of the constructor means in finddialog.cpp ?

    FindDialog::FindDialog(QWidget *parent)
        : QDialog(parent)
    

    This is class inheritance as far as I understand it


  • Qt Champions 2016

    Hi
    FindDialog::FindDialog(QWidget *parent)
    : QDialog(parent) <---- here we call base class constructor with the parent.

    This is need to setup the part of the class we get from the base class.

    if u have
    class A :B

    then some of A is from B and it might need to do stuff in its constructor.
    so we call it also.



  • Tell me if I am wrong...

    QDialog is derived from QWidget.

    The FindDialog constructor is typical of Qt widget classes. The parent parameter specifies the parent widget. The default is a null pointer meaning that the dialog has no parent

    when we have

    FindDialog::FindDialog(QWidget *parent)
       : QDialog(parent)
    

    we just say that QDialog is the parent of FindDialog and as mentioned above QWidget is the parent of QDialog.



  • @SweetOrange

    Just to be sure that you don't mix up "C++ parent class" and "Qt parent mechanism".

    In pure C++, a class can inherit from a parent class:

    class QDialog: public QWidget {}
    

    Here the QDialog inherits from QWidget.

    In Qt, there is a mechanism that makes your life easier for memory managment. You give your class a parent to let it handle the memory in destruction. If you code in C++, you may know that there is no garbage collector as there is in other languages (Java for instance). So as soon as you create an object dynamically - on the heap - using "new" operator, you are responsible for it's destruction using "delete". Qt enables you not to worry about deletion: give your object a parent, and the memory will be handle at its destruction.

    FindDialog::FindDialog(QWidget *parent): QDialog(parent)
    

    Here we call the parent constructor (QDialog) with the parent parameter and we let Qt handle destruction, as well as a provide a way to potential future inherited classes to enjoy this mechanism.


  • Lifetime Qt Champion

    Hi,

    To add to my fellows, you should move:

    #include <QDialog>
    #include <QLabel>
    #include <QLineEdit>
    #include <QCheckBox>
    #include <QPushButton>
    

    in your implementation file. That's were they are needed in your case.

    Note that you are using #include <QtGui>, while good for rapid prototyping it's a module wide include which means it will pull in all headers from said module thus your compilation time will increase since all files are going to be parsed.


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.