Помилка "Поле має неповний тип"


79

У моєму файлі заголовка сталася помилка:

field "ui" has incomplete type.

Я намагався зробити uiвказівник, але це не працює. Я не думаю, що мені потрібно це робити, тому що я вже визначив своє MainWindowClassу просторі імен Ui. Це мій 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

3
Крім того, це абсолютно не пов'язано з QtCreator
weberc2

Відповіді:


104

Ви використовуєте пряму декларацію для типу MainWindowClass. Це добре, але це також означає, що ви можете оголосити лише вказівник або посилання на цей тип. В іншому випадку компілятор не уявляє, як розподілити батьківський об'єкт, оскільки він не знає розмір оголошеного вперед типу (або якщо він насправді має безпараметричний конструктор тощо)

Отже, ви або хочете:

// forward declaration, details unknown
class A;

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

Або, якщо ви не можете використовувати вказівник або посилання ....

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

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

У певний момент компілятору потрібно знати подробиці A.

Якщо ви зберігаєте лише вказівник на, Aтоді йому не потрібні ці деталі при оголошенні B. Вони потребують їх у певний момент (коли б ви насправді не перенаправляли вказівник A), який, ймовірно, буде у файлі реалізації, де вам потрібно буде включити заголовок, що містить оголошення класу 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();
}

Я хотів би лише додати, що ви можете досягти тієї ж мети за допомогою шаблонів. Перший приклад (rextester.com/TLPEJW28982) оголошує A як глобальний клас і визначає його після визначення шаблону класу. Як перевагу, вам не потрібно турбуватися про те, де ви визначаєте структуру А, і уникаєте зчеплення. Другий приклад (rextester.com/PJCD69132) розглядає A як вкладену структуру B. Причина, по якій це компілюється та працює нормально, полягає у двофазному пошуку, який відбувається під час створення екземпляра шаблону. Для отримання детальної інформації зверніться до шаблонів C ++: Повний посібник, 10.3.1 Двофазне пошук.
Constantinos Glynos

20

Проблема полягає в тому, що у вашому uiресурсі використовується пряме оголошення класу Ui::MainWindowClass, звідси помилка "неповний тип".

Включення файлу заголовка, у якому оголошено цей клас, вирішить проблему.

РЕДАГУВАТИ

На основі вашого коментаря, такий код:

namespace Ui
{
    class MainWindowClass;
}

це НЕ оголосити клас. Це пряма декларація , що означає, що клас буде існувати в певний момент, під час зв’язку.
По суті, він просто повідомляє компілятору, що тип буде існувати, і що він не повинен про це попереджати.

Але клас потрібно десь визначити .

Зауважте, це може працювати, лише якщо у вас є вказівник на такий тип.
Ви не можете мати статично виділений екземпляр неповного типу.

Отже, ви насправді хочете неповний тип, і тоді вам слід оголосити свого uiчлена як покажчик:

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

Або ви хочете статично виділений екземпляр Ui::MainWindowClass, а потім його потрібно оголосити. Ви можете зробити це в іншому файлі заголовка (зазвичай у кожному класі є один файл заголовка).
Але просто змінивши код на:

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

також буде працювати.

Зверніть увагу на різницю між двома деклараціями. Перший використовує пряме оголошення, тоді як другий фактично оголошує клас (тут без властивостей і методів).


Дякую. Це єдиний у мене файл заголовка. Я оголосив клас MainWindowClass вище, відразу після #includes. Чи слід створювати новий файл заголовка?
IsabellaW

Немає прямого оголошення, проблема полягає у оголошенні змінної-члена того самого типу, яка наразі визначається.
Ed S.

Неважливо, я загубився на фігурних дужках, не бачив, що це насправді клас, оголошений нижче прямої декларації MainWindowClass.
Ed S.

Ui::MainWindowClass(змінна члена) визначена в MainWindow. Не зовсім той самий вид.
Macmade

Однак я б не вважав, що якщо ви використовуєте декларацію forward, тоді змінна повинна бути покажчиком або посиланням на тип оголошеного вперед. В іншому випадку, якщо це неприпустимо, вам потрібно включити заголовок, який містить декларацію типу.
Ed S.
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.