【问题标题】:Why is the order of #include statements so important?为什么#include 语句的顺序如此重要?
【发布时间】:2017-04-04 16:37:52
【问题描述】:

在我的程序(wxWidgets,code::blocks)中,我注意到一些我不太理解的行为。如果我这样写我的标题:

#ifndef RECORDTHREAD_H
#define RECORDTHREAD_H

#include <wx/thread.h>
#include <wx/dialog.h>
#include <wx/string.h>

#include "Serial.h"

class RecordTrackDialog;

class RecordThread : public wxThread
{
    public:
        RecordThread(RecordTrackDialog* parent);
        virtual ~RecordThread();
    protected:
    private:
        virtual ExitCode Entry();

        Serial m_serial;
};

#endif // RECORDTHREAD_H

(使用#include "Serial.h" 作为最后一个包含语句)一切正常,但是当我像这样更改包含语句时:

#ifndef RECORDTHREAD_H
#define RECORDTHREAD_H

#include "Serial.h"

#include <wx/thread.h>
#include <wx/dialog.h>
#include <wx/string.h>

我收到这样的错误:

||=== Build: Debug in WindowsDgpsGUI (compiler: GNU GCC Compiler) ===|
C:\wxWidgets-3.0.2\include\wx\msw\winundef.h||In function 'HWND__* CreateDialog(HINSTANCE, LPCTSTR, HWND, DLGPROC)':|
C:\wxWidgets-3.0.2\include\wx\msw\winundef.h|38|error: cannot convert 'LPCTSTR {aka const char*}' to 'LPCWSTR {aka const wchar_t*}' for argument '2' to 'HWND__* CreateDialogParamW(HINSTANCE, LPCWSTR, HWND, DLGPROC, LPARAM)'|
C:\wxWidgets-3.0.2\include\wx\msw\winundef.h||In function 'HFONT__* CreateFont(int, int, int, int, int, DWORD, DWORD, DWORD, DWORD, DWORD, DWORD, DWORD, DWORD, LPCTSTR)':|
C:\wxWidgets-3.0.2\include\wx\msw\winundef.h|69|error: cannot convert 'LPCTSTR {aka const char*}' to 'LPCWSTR {aka const wchar_t*}' for argument '14' to 'HFONT__* CreateFontW(int, int, int, int, int, DWORD, DWORD, DWORD, DWORD, DWORD, DWORD, DWORD, DWORD, LPCWSTR)'|
C:\wxWidgets-3.0.2\include\wx\msw\winundef.h||In function 'HWND__* CreateWindow(LPCTSTR, LPCTSTR, DWORD, int, int, int, int, HWND, HMENU, HINSTANCE, LPVOID)':|
C:\wxWidgets-3.0.2\include\wx\msw\winundef.h|94|error: cannot convert 'LPCTSTR {aka const char*}' to 'LPCWSTR {aka const wchar_t*}' for argument '2' to 'HWND__* CreateWindowExW(DWORD, LPCWSTR, LPCWSTR, DWORD, int, int, int, int, HWND, HMENU, HINSTANCE, LPVOID)'|
C:\wxWidgets-3.0.2\include\wx\msw\winundef.h||In function 'HMENU__* LoadMenu(HINSTANCE, LPCTSTR)':|
C:\wxWidgets-3.0.2\include\wx\msw\winundef.h|111|error: cannot convert 'LPCTSTR {aka const char*}' to 'LPCWSTR {aka const wchar_t*}' for argument '2' to 'HMENU__* LoadMenuW(HINSTANCE, LPCWSTR)'|
C:\wxWidgets-3.0.2\include\wx\msw\winundef.h||In function 'HWND__* FindText(LPFINDREPLACE)':|
C:\wxWidgets-3.0.2\include\wx\msw\winundef.h|126|error: cannot convert 'LPFINDREPLACE {aka FINDREPLACEA*}' to 'LPFINDREPLACEW {aka FINDREPLACEW*}' for argument '1' to 'HWND__* FindTextW(LPFINDREPLACEW)'|
C:\wxWidgets-3.0.2\include\wx\msw\winundef.h||In function 'HICON__* LoadIcon(HINSTANCE, LPCTSTR)':|
C:\wxWidgets-3.0.2\include\wx\msw\winundef.h|311|error: cannot convert 'LPCTSTR {aka const char*}' to 'LPCWSTR {aka const wchar_t*}' for argument '2' to 'HICON__* LoadIconW(HINSTANCE, LPCWSTR)'|
C:\wxWidgets-3.0.2\include\wx\msw\winundef.h||In function 'HBITMAP__* LoadBitmap(HINSTANCE, LPCTSTR)':|
C:\wxWidgets-3.0.2\include\wx\msw\winundef.h|324|error: cannot convert 'LPCTSTR {aka const char*}' to 'LPCWSTR {aka const wchar_t*}' for argument '2' to 'HBITMAP__* LoadBitmapW(HINSTANCE, LPCWSTR)'|
||=== Build failed: 7 error(s), 0 warning(s) (0 minute(s), 1 second(s)) ===|

我不太了解这种行为,因为标头是一个线程,错误来自使用该线程的对话框。谁能解释为什么 C++(或 wxWidgets)会这样?

编辑:

Serial.h 的包含

#ifndef SERIAL_H
#define SERIAL_H

#include <windows.h>
//#include <wx/msw/winundef.h>
#include <stdio.h> // necessary for sprintf
#include <string>

这似乎符合 Marco 的评论,但我不能包含该部分...

【问题讨论】:

  • 什么是Serial.h?当您使用双引号时,它应该意味着它是您项目中的一个文件 - 如果您自己的头文件依赖于其他头文件,那么您写的 Serial.h 不正确。 Serial.h 应该对自己的包含负责。
  • 可能相关:wiki.wxwidgets.org/…
  • 向我们展示 Serial.h.
  • 表示有一个或多个表头损坏。在正确编写的代码中,顺序应该无关紧要。
  • 这意味着Serial.h 中的某些内容依赖于一个或多个wx 标头中的声明或定义。 Serial.h 需要包含它所依赖的那些标头,或者您始终需要确保 #include "Serial.h" 仅在包含 wx 标头后才这样做的其他文件。

标签: c++ wxwidgets


【解决方案1】:

问题在于&lt;windows.h&gt; 定义了不同的符号,具体取决于是否定义了UNICODE 标准(即在Windows 下)宏。如果您首先包含 wxWidgets 标头,默认情况下假定 Unicode 构建,它们会在包含 &lt;windows.h&gt; 之前为您定义 UNICODE,一切都很好。

如果您首先包含&lt;windows.h&gt;,那么此时未定义UNICODE,但是当您稍后包含wxWidgets 标头时,它们会使用wxUSE_UNICODE=1(这也是默认值)导致您的编译问题'正在观察。

确保您永远不会遇到此类问题的最简单方法是在项目设置或 makefile 中全局定义 UNICODE

【讨论】:

    【解决方案2】:

    正如 cmets 指出的那样,这可能是因为 Serial.h 依赖于您包含主文件 (RecordThread.h?) 的 3 个 wx\xxxx 文件中的定义。

    像这样将它们添加到Serial.h,您将拥有与以前相同的功能。

    #ifndef SERIAL_H
    #define SERIAL_H
    
    #include <wx/thread.h>
    #include <wx/dialog.h>
    #include <wx/string.h>
    
    #include <windows.h>
    //#include <wx/msw/winundef.h>
    #include <stdio.h> // necessary for sprintf
    #include <string>
    

    如果您要正确执行此操作,您应该在 Serial.h 中确定您实际需要的一个(或多个),但添加所有这些应该可以清除错误。

    编译预处理器处理#include 时,就像指定文件被复制到主文件#include 的位置一样。那是一个递归过程,文件可以#include文件,#include文件等等。

    对于#include 和一般的预处理器,新手似乎有很多神秘色彩。它实际上只是一个文本操作工具。

    在现代 C 语言的许多地方,您必须在使用之前声明(引入/定义)符号/标识符。如果该符号/标识符在头文件中声明,您应该在使用它之前#include 该文件。

    【讨论】:

      【解决方案3】:

      只是一些常见的后续做法,这可能无法回答您的问题,但会为您指明正确的方向:

      • 总是让你不要#include同一个文件中的同一个头文件,这意味着混淆但使用下图理解它:

      有效:

      -#include "some.h"
      -#include "some1.h"
      

      有效:

      -#include "some.h"
      --#include "some1.h"
      

      无效:

      -#include "some.h"
      -#include "some1.h"
      --#include "some.h"
      

      所以除非你知道自己在做什么,否则不要嵌套..

      解决您的问题的一种可能方法是将wx\thread.hwx\dialog.hwx\string.h 包含在Serial.h 中,试试吧,然后告诉我

      为什么要按顺序调用#include

      因为这样理解才有意义,阿里需要打扫修车,阿里不知道怎么修车,所以阿里会叫技工修车,所以这里可以说(阿里依赖Mechanic):

      - #include "mechanic.h"
      
      - #include "Ali.h"
      

      【讨论】:

      • 我明白你的意思,但是从每个类的作用来看,将这些标题包含在Serial.h 中没有多大意义,因为 Serial 只是打开并从串行通信中读取,并且是某人的类else 写的,Record 部分是真正需要这些的……Record 打开串行通信,解析输入并进一步处理它。
      • 有几种情况,没有任何意义,但无论如何你都必须这样做,因为这是唯一的解决方法,尝试调用它们以便它们在单个文件中相互依赖,可能是main还是任何头文件,为什么按顺序调用很重要,我们在回答中讨论
      • 这是错误的。如果 some1.h 需要 some.h 中的名称,那么 some1.h 应该#include some.h。如果源文件需要来自 some.h 和 some1.h 的名称,那么它应该 #include 这两个文件。标记为“无效:”的代码非常好,只要 some.h 具有适当的包含防护。
      • @PeteBecker 最好不要将初学者与guards 东西混淆,最好的做法是尽量不要使用它们,我错了吗?
      • 你完全错了。包含守卫是 C 和 C++ 编程的基本工具。不使用它们会导致神秘的相互依赖、顺序依赖和令人困惑的错误消息。
      猜你喜欢
      • 2012-10-01
      • 1970-01-01
      • 1970-01-01
      • 2016-11-20
      • 1970-01-01
      • 2012-03-26
      • 2011-11-28
      • 2013-05-18
      相关资源
      最近更新 更多