Error "El campo tiene un tipo incompleto"

79

Hay un error en mi archivo de encabezado:

field "ui" has incomplete type.

He intentado hacer uiun puntero, pero no funciona. No creo que deba hacer eso porque ya he definido my MainWindowClassen el espacio de nombres Ui. Este es mi 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
IsabelW
fuente
3
Además, esto no tiene ninguna relación con QtCreator
weberc2

Respuestas:

104

Está utilizando una declaración de reenvío para el tipo MainWindowClass. Eso está bien, pero también significa que solo puede declarar un puntero o una referencia a ese tipo. De lo contrario, el compilador no tiene idea de cómo asignar el objeto principal, ya que no conoce el tamaño del tipo declarado hacia adelante (o si realmente tiene un constructor sin parámetros, etc.)

Entonces, o quieres:

// forward declaration, details unknown
class A;

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

O, si no puede usar un puntero o una referencia ...

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

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

En algún momento, el compilador necesita conocer los detalles de A.

Si solo está almacenando un puntero a, Aentonces no necesita esos detalles cuando declara B. Los necesita en algún momento (siempre que elimine la referencia del puntero A), que probablemente estará en el archivo de implementación, donde deberá incluir el encabezado que contiene la declaración de la clase 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.
fuente
Solo me gustaría agregar que puede lograr el mismo objetivo a través de plantillas. El primer ejemplo (rextester.com/TLPEJW28982) declara A como una clase global y la define después de la definición de plantilla de clase. Como ventaja, no tiene que preocuparse por dónde define la estructura A y evita el acoplamiento. El segundo ejemplo (rextester.com/PJCD69132) considera A como una estructura anidada de B. La razón por la que esto se compila y funciona bien es debido a la búsqueda en dos fases que ocurre cuando se crea una instancia de una plantilla. Para obtener más detalles, consulte Plantillas de C ++: la guía completa, 10.3.1 Búsqueda en dos fases.
Constantinos Glynos
20

El problema es que su uipropiedad usa una declaración de clase hacia adelanteUi::MainWindowClass , de ahí el error "tipo incompleto".

Incluir el archivo de encabezado en el que se declara esta clase solucionará el problema.

EDITAR

Basado en su comentario, el siguiente código:

namespace Ui
{
    class MainWindowClass;
}

no no declarar una clase. Es una declaración hacia adelante , lo que significa que la clase existirá en algún momento, en el momento del enlace.
Básicamente, solo le dice al compilador que el tipo existirá y que no debería advertir sobre él.

Pero la clase debe definirse en alguna parte .

Tenga en cuenta que esto solo puede funcionar si tiene un puntero a dicho tipo.
No puede tener una instancia asignada estáticamente de un tipo incompleto.

Entonces, o realmente desea un tipo incompleto, y luego debe declarar su uimiembro como un puntero:

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;
};

O desea una instancia de asignada estáticamente Ui::MainWindowClass, y luego debe declararse. Puede hacerlo en otro archivo de encabezado (generalmente, hay un archivo de encabezado por clase).
Pero simplemente cambiando el código a:

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;
};

también funcionará.

Note la diferencia entre las dos declaraciones. Primero usa una declaración hacia adelante, mientras que el segundo realmente declara la clase (aquí sin propiedades ni métodos).

Macmade
fuente
Gracias. Este es el único archivo de encabezado que tengo. He declarado la clase MainWindowClass arriba, justo después de #includes. ¿Debo crear un nuevo archivo de encabezado?
IsabellaW
No hay declaración de reenvío, el problema es declarar una variable miembro del mismo tipo que se está definiendo actualmente.
Ed S.
No importa, me perdí con las llaves, no vi que esta era en realidad una clase declarada debajo de la declaración de avance de MainWindowClass.
Ed S.
Ui::MainWindowClass(variable miembro) se define en MainWindow. No es realmente del mismo tipo.
Macmade
Sin embargo, no lo haría, si está utilizando una declaración hacia adelante, entonces la variable debe ser un puntero o referencia al tipo declarado hacia adelante. De lo contrario, si eso es inaceptable, debe incluir el encabezado que contiene la declaración del tipo.
Ed S.