为什么#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.h
、wx\dialog.h
、wx\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
。
在我的程序(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.h
、wx\dialog.h
、wx\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
。