【问题标题】:"Field has incomplete type" error“字段类型不完整”错误
【发布时间】:2012-09-10 01:41:28
【问题描述】:

我的头文件有错误:

field "ui" has incomplete type.

我尝试将ui 设为指针,但这不起作用。我认为我不需要这样做,因为我已经在命名空间Ui 中定义了我的MainWindowClass。这是我的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

【问题讨论】:

  • 另外,这与 QtCreator 完全无关

标签: c++


【解决方案1】:

问题是您的ui 属性使用了Ui::MainWindowClass 类的forward declaration,因此出现“不完整类型”错误。

包含声明此类的头文件将解决问题。

编辑

根据您的评论,以下代码:

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

也可以。

注意这两个声明之间的区别。第一个使用前向声明,而第二个实际上声明了类(这里没有属性也没有方法)。

【讨论】:

  • 谢谢。这是我唯一的头文件。我已经在#includes 之后声明了上面的MainWindowClass 类。我应该创建一个新的头文件吗?
  • 没有前向声明,问题是声明了一个与当前正在定义的类型相同的成员变量。
  • 没关系,我在花括号上迷路了,没有看到这实际上是在MainWindowClass 的前向声明下面声明的类。
  • Ui::MainWindowClass(成员变量)在MainWindow中定义。不完全一样。
  • 但是,如果您使用前向声明,那么我不会那样做,那么变量必须是指向前向声明类型的指针或引用。否则,如果这是不可接受的,则需要包含包含类型声明的标头。
【解决方案2】:

您正在使用 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 声明为全局类,并在类模板定义之后定义它。作为一个优势,您不必担心在哪里定义 struct A 并避免耦合。第二个示例 (rextester.com/PJCD69132) 将 A 视为 B 的嵌套结构。它编译和运行良好的原因是因为在实例化模板时发生了两阶段查找。有关详细信息,请参阅 C++ 模板:完整指南,10.3.1 两阶段查找。
猜你喜欢
  • 2011-04-29
  • 1970-01-01
  • 2016-09-07
  • 1970-01-01
  • 2018-05-11
  • 2012-05-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多