为什么#include 语句的顺序如此重要?

Why is the order of #include statements so important?

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

#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" 作为最后一个 include 语句)一切正常,尽管当我像这样更改 include 语句时:

#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)) ===|

我不太理解这种行为,因为 header 是一个线程,错误来自使用该线程的对话框。谁能解释为什么 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 依赖于包含主文件的 3 wx\xxxx 个文件中的定义(RecordThread.h?)。

像这样将它们添加到 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 的许多地方,您必须在使用前声明 (introduce/define) a symbol/identifier。如果在头文件中声明了 symbol/identifier,则在使用它之前应该 #include 该文件。

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

  • Always make you you don't #include同一文件中的相同头文件,这意味着混淆但使用下图理解它:

有效:

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

有效:

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

无效:

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

所以不要嵌套除非你知道你在做什么..

您的问题的一个可能解决方案是在 Serial.h 中包含 wx\thread.hwx\dialog.hwx\string.h,尝试一下,然后告诉我

为什么按顺序调用 #include 很重要?

因为只有这样理解才有意义,阿里需要清洁修理他的车,阿里不会修理他的车,所以阿里会打电话给机械师来修理,所以这里我们可以说那(阿里依赖Mechanic):

- #include "mechanic.h"

- #include "Ali.h"

问题在于<windows.h>根据是否定义了UNICODE标准(在Windows下)宏定义了不同的符号。如果您首先包含 wxWidgets headers,默认情况下假定 Unicode 构建,它们会在包含 <windows.h> 之前为您定义 UNICODE,一切都很好。

如果你先包含<windows.h>,那么此时UNICODE没有定义,但是当你稍后包含wxWidgets时headers他们使用wxUSE_UNICODE=1(即,再次,默认值)导致您观察到的编译问题。

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