【问题标题】:Class declaration with no definition in C++C++ 中没有定义的类声明
【发布时间】:2012-03-17 09:58:23
【问题描述】:

我对 C++ 没有太多经验,我对 Qt 文档中的以下几行有疑问:http://qt-project.org/doc/qt-4.8/mainwindows-application-mainwindow-h.html(顶部注释后的第 4-6 行)

 class QAction;
 class QMenu;
 class QPlainTextEdit;

由于 QAction、QMenu 和 QPlainTextEdit 是 Qt 的库类(单击它们会导致它们的文档页面),它们不应该使用 '#include' 包含吗?用关键字“class”声明它们有什么目的?在我看来,编译器会认为它是一个全新的类,与库类 QAction 无关。

但是,“mainwindow.cpp”文件 (http://qt-project.org/doc/qt-4.8/mainwindows-application-mainwindow-cpp.html) 不包含任何 QAction 类的定义,尽管代码中使用了它的对象。

这是怎么回事?

编辑:问题的进一步解释

查看类 MainWindow (http://qt-project.org/doc/qt-4.8/mainwindows-application-mainwindow-cpp.html) 的 createActions 方法。这里正在创建 QAction 类的对象,但我找不到 QAction 类的定义。

【问题讨论】:

    标签: c++


    【解决方案1】:

    声明一个类,而不是定义它,足以让编译器意识到某个名称引用一个类型的事实,并且允许你在某些设置中使用类型。具体来说,已声明但尚未定义的类型是不完整类型,您可以通过多种方式使用不完整类型:

    • 在函数声明中,函数的返回类型和参数类型可能不完整(但当然不是在函数的定义中)。*

    • 如果 T 是不完整类型,则 T*T& 以及它们的 CV 限定版本是完整类型。

    所以你可以写如下:

    class Foo;  // we only need to know that `Foo` names a type.
    
    struct Bar { Foo * p; };   // complete definition of `Bar`.
    
    Foo mangle(Foo x, Foo y);  // function declaration
    

    您可以随意声明一个类或任何类型。唯一必须是唯一的就是定义(由此而来的“一个定义规则”)。

    *) 除了一个例外:void 始终不完整,您可以通过不返回任何内容来返回 void 类型。

    【讨论】:

    • 好的。我明白这个事实。但是,如果您查看类 MainWindow (qt-project.org/doc/qt-4.8/…) 中 createActions 方法的第一行,则会调用 QAction 类的构造函数,尽管在任何地方都没有定义任何构造函数。如何调用尚未定义的方法?
    • 在MainWindow文件中,他们#include 。我没有查过,但这不是真正的QAction声明吗?
    【解决方案2】:

    那些被称为“forward declarations”的类。它用于使编译器知道这些符号是指类,这些类将在以后定义。这些可以在头文件中用于将符号声明为一个类,从而避免包含其头文件,这在某些情况下可能需要(例如循环包含)。

    无论如何,您不能总是使用前向声明,因为编译器可能需要知道类是如何定义的。在其他情况下,编译器只需要知道它实际上是一个类这一事实。

    编辑:要回答您添加的以下问题,请查看您可以在 *qt_install_dir*/include/QtGui 中找到的 QtGui 文件。在我的 Qt 4.8 安装中,我看到了里面:

    #include "qaction.h"
    

    然后查看 qaction.h:您将看到 QAction 类的完整定义。头文件 QtGui 包含在 mainwindow.cpp 的开头,这使得 QAction 成为一个完整的类型。

    【讨论】:

      【解决方案3】:

      类的类型完全由它的完全限定名称(不确定这是官方术语,但无论如何)决定,其中包括它的命名空间和它的名称。无论是来自头文件还是简单地声明 in-situ

      实际上,头文件的包含基本上只是将该头文件的文本插入到包含出现的源代码的位置。效果与使用您喜欢的文本编辑器将#include 替换为头文件的内容相同。无论哪种方式,编译器本身只会看到一个标记流。

      使用前向声明的主要目的是通过减少它必须解析的文本量和它在 RAM 中建立的符号表的大小来加速编译器。我发现这些天在我自己的工作中基本上不需要这种做法,但非常大的项目可能仍然会受益。

      【讨论】:

        【解决方案4】:

        几天前我刚刚研究了这个例子。所以这是我认为你需要这些的结论:

        我猜如果你查看 .pro 文件,你会看到这样一行:

        QT += core
        QT += gui
        

        所以问题是您告诉 Qt 翻译器和编译器您正在使用 QtGui 和 QtCore 模块,并且在这些模块中您定义了 QAction 等。因此,作为“黑客”(因为他们的 IDE 直到编译过程的后期才能自动检测到这一点),您预先声明了类以避免在编译阶段出现任何错误。最后Qt插入模块,一切正常。

        我第一次这样做时也让我头疼,但这是我能得到的唯一合乎逻辑的解释。

        如果您不预先声明该类,您将收到一条错误消息,指出它找不到对该类的任何引用等。

        编辑: 鉴于您最近的更新,我建议您去 YouTube 并搜索昵称下的用户:voidrealms。他有不错的 C++ Qt 视频教程和解释,在那里他还展示了 QMenu、QAction 等。他有 150 多个关于 Qt C++ 编程的视频,涵盖了从基本的 Hello World 到网络编程甚至更远的所有内容......看看他,他还有其他教程

        【讨论】:

        • 在我看来,它也必须是这样的。但这是糟糕且令人困惑的编程。如果这是真的,文档应该清楚地说明这一点。
        • 虽然 Qt 是一个很好的跨平台框架,有不错的文档,但我认为这些示例应该比它们更具描述性。然而,没有什么比努力学习更好的了:反复试验:P
        • 另外,如果你注意到你不能使用网络模块或 sql 模块,除非你在 .pro 文件中指定它们,即使你将它们包含在程序中,这有点令人困惑次
        • 我没有真正看到这里的黑客行为。 .pro 文件中的 QT 变量仅根据您声明要使用的模块(在 g++ 命令行中添加 -I、-L 和 -l)自动插入 Makefile 包含路径并链接到正确的 Qt 库,就像您一样在任何 C/C++ 项目中执行。这对于避免链接到未使用的模块非常有用。在此之后,就像在任何其他 C/C++ 项目中一样,您必须在每个源文件(本例中为 qaction.h)中包含正确的头文件,该文件包含在 QtGuid 头文件中。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-07-21
        • 2011-06-13
        • 1970-01-01
        • 1970-01-01
        • 2013-05-28
        • 2017-07-14
        相关资源
        最近更新 更多