Fehler "Feld hat unvollständigen Typ"

79

In meiner Header-Datei ist ein Fehler aufgetreten:

field "ui" has incomplete type.

Ich habe versucht, uieinen Zeiger zu erstellen, aber das funktioniert nicht. Ich glaube nicht, dass ich das tun muss, weil ich meine bereits MainWindowClassim Namespace definiert habe Ui. Das ist mein mainwindow.h:

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QtGui/QMainWindow>
#include "ui_mainwindow.h"

namespace Ui {
    class MainWindowClass;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

    public:
        MainWindow(QWidget *parent = 0, Qt::WFlags flags=0);
        ~MainWindow();

    public slots:
        void slideValue(int);
    private:
        Ui::MainWindowClass ui; //error line
};

#endif // MAINWINDOW_H
IsabellaW
quelle
3
Auch dies ist völlig unabhängig von QtCreator
weberc2

Antworten:

104

Sie verwenden eine Forward-Deklaration für den Typ MainWindowClass. Das ist in Ordnung, bedeutet aber auch, dass Sie nur einen Zeiger oder Verweis auf diesen Typ deklarieren können. Andernfalls hat der Compiler keine Ahnung, wie das übergeordnete Objekt zugewiesen werden soll, da er die Größe des vorwärts deklarierten Typs nicht kennt (oder ob er tatsächlich einen parameterlosen Konstruktor usw. hat).

Also wollen Sie entweder:

// forward declaration, details unknown
class A;

class B {
  A *a;  // pointer to A, ok
};

Oder wenn Sie keinen Zeiger oder keine Referenz verwenden können ...

// declaration of A
#include "A.h"

class B {
  A a;  // ok, declaration of A is known
};

Irgendwann muss der Compiler die Details von kennen A.

Wenn Sie nur einen Zeiger auf speichern, werden Adiese Details beim Deklarieren nicht benötigt B. Sie werden irgendwann benötigt (wann immer Sie den Zeiger tatsächlich dereferenzieren A), wahrscheinlich in der Implementierungsdatei, in der Sie den Header einfügen müssen, der die Deklaration der Klasse enthält A.

// B.h
// header file

// forward declaration, details unknown
class A;

class B {
public: 
    void foo();
private:
  A *a;  // pointer to A, ok
};

// B.cpp
// implementation file

#include "B.h"
#include "A.h"  // declaration of A

B::foo() {
    // here we need to know the declaration of A
    a->whatever();
}
Ed S.
quelle
Ich möchte nur hinzufügen, dass Sie das gleiche Ziel über Vorlagen erreichen können. Das erste Beispiel (rextester.com/TLPEJW28982) deklariert A als globale Klasse und definiert es nach der Definition der Klassenvorlage. Als Vorteil müssen Sie sich keine Gedanken darüber machen, wo Sie Struktur A definieren, und Sie vermeiden eine Kopplung. Im zweiten Beispiel (rextester.com/PJCD69132) wird A als verschachtelte Struktur von B betrachtet. Der Grund dafür, dass dies kompiliert und ordnungsgemäß ausgeführt wird, liegt in der zweiphasigen Suche, die beim Instanziieren einer Vorlage auftritt. Weitere Informationen finden Sie unter C ++ - Vorlagen: Das vollständige Handbuch, 10.3.1 Zweiphasensuche.
Constantinos Glynos
20

Das Problem ist, dass Ihre uiEigenschaft eine Vorwärtsdeklaration der Klasse verwendet Ui::MainWindowClass, daher der Fehler "unvollständiger Typ".

Das Einfügen der Header-Datei, in der diese Klasse deklariert ist, behebt das Problem.

BEARBEITEN

Basierend auf Ihrem Kommentar den folgenden Code:

namespace Ui
{
    class MainWindowClass;
}

ist nicht eine Klasse deklarieren. Es ist eine Vorwärtsdeklaration , was bedeutet, dass die Klasse irgendwann zum Zeitpunkt der Verknüpfung vorhanden sein wird.
Grundsätzlich teilt es dem Compiler nur mit, dass der Typ vorhanden sein wird und dass er nicht davor warnen sollte.

Aber die Klasse muss irgendwo definiert werden .

Beachten Sie, dass dies nur funktionieren kann, wenn Sie einen Zeiger auf einen solchen Typ haben.
Sie können keine statisch zugewiesene Instanz eines unvollständigen Typs haben.

Entweder möchten Sie tatsächlich einen unvollständigen Typ, und dann sollten Sie Ihr uiMitglied als Zeiger deklarieren :

namespace Ui
{
    // Forward declaration - Class will have to exist at link time
    class MainWindowClass;
}

class MainWindow : public QMainWindow
{
    private:

        // Member needs to be a pointer, as it's an incomplete type
        Ui::MainWindowClass * ui;
};

Oder Sie möchten eine statisch zugewiesene Instanz von Ui::MainWindowClass, und dann muss sie deklariert werden. Sie können dies in einer anderen Header-Datei tun (normalerweise gibt es eine Header-Datei pro Klasse).
Aber ändern Sie einfach den Code in:

namespace Ui
{
    // Real class declaration - May/Should be in a specific header file
    class MainWindowClass
    {};
}


class MainWindow : public QMainWindow
{
    private:

        // Member can be statically allocated, as the type is complete
        Ui::MainWindowClass ui;
};

wird auch funktionieren.

Beachten Sie den Unterschied zwischen den beiden Erklärungen. Der erste verwendet eine Vorwärtsdeklaration, während der zweite die Klasse tatsächlich deklariert (hier ohne Eigenschaften oder Methoden).

Macmade
quelle
Vielen Dank. Dies ist die einzige Header-Datei, die ich habe. Ich habe die Klasse MainWindowClass oben direkt nach dem #includes deklariert. Soll ich eine neue Header-Datei erstellen?
IsabellaW
Es gibt keine Vorwärtsdeklaration. Das Problem besteht darin, eine Mitgliedsvariable desselben Typs zu deklarieren, der gerade definiert wird.
Ed S.
Egal, ich habe mich in geschweiften Klammern verlaufen und nicht gesehen, dass dies tatsächlich eine Klasse ist, die unter der Vorwärtsdeklaration von deklariert ist MainWindowClass.
Ed S.
Ui::MainWindowClass(Mitgliedsvariable) ist definiert in MainWindow. Nicht wirklich die gleiche Art.
Macmade
Wenn Sie jedoch eine Vorwärtsdeklaration verwenden, muss die Variable ein Zeiger oder eine Referenz auf den vorwärts deklarierten Typ sein. Andernfalls müssen Sie, wenn dies nicht akzeptabel ist, den Header einfügen, der die Deklaration des Typs enthält.
Ed S.