简单问题:两个头文件能互相包含吗?

时间:2021-05-16 10:02:11
如我在a.h中#include "b.h"
在b.h中#include "a.h"
行不行?为什么?

23 个解决方案

#1


可以可以,为什么不可以呢!

你说说理由。

#2


多数情况会产生重复定义的错误,尽量不这么用。

#3


同意,你最好在所用的*.cpp中#include "*.h"

#4


其实*.h和*.cpp是一样的,不过是为了方便,才有两个不同类型的文件的。

#5


我是在两个类中都要用到另一个,
而都在.h文件利用到,编译时出错。
比如:你在CXDoc.h中包含#include "CXView.h"
就会看到错误。

#6


看看你的程序逻辑,能不能重新设计一下,最好不要这样用

#7


有些东西不是逻辑的问题,这种情况有些时候是一定会出现的
解决的办法简单不过
#pragma once
include文件中加上这一行随便怎么包含吧

#8


应该说不允许
c和c++是静态类型检查的语言,也就是说,在编译时,所有的类型都必须确定,然后分配内存。而你的h文件,就是类型声明文件,告诉编译程序你定义了什么类型。而在一个h文件中包含的其他h文件,也就是你在这个h文件中所定义的类型(也许是class)中包含有其他的类型,这些信息编译程序都必须在编译是知道。如果你的两个h文件相互包含,那么在编译是,编译程序就无法确定每个类型的大小了,因为互为依赖。

#9


看来我看的书还是不够,我会继续努力的,以好和大家讨论学习。

#10


这种问题在考试时才会碰到吧!

#11


我觉得完全可以阿,

#12


你可以将两个.h 文件写在一块啊:)
我很讨厌互相包含这种东东
我觉得这是逻辑中的缺陷

#13


不行

#14


可以!
但一定要有存在标识!
例如:a。h
#if !defined(唯一标识该文件的字符串)
  内容
#endif

#15


还在讨论啊,这根本不是什么逻辑的关系,假如有这样一个例子,两个类A,B需要互相调用,最简单的方法就是互相包含对方的头文件,这并没有错,除非你要把A,B写在一个文件中,或者使用interface这样的东东。
互相包含是允许的,为了防止编译时的错误,所以有#pragma once这样的声明,无论头文件被包含多少次,都只编译1次,只要在头文件中加上这一行就可以随意互相包含了,其实在一定的情况下互相包含可以让你的工作更简单,什么东西都是视情况而定,就像goto语句,很多书上已经把它扁得一文不值了,但是假如遇到内联汇编的时候,goto也可以做一些巧妙的工作,看你懂不懂得灵活运用了。所以不要认为什么东西没有用而厌恶它,什么东西都是有用的,只有你不懂的时候它才是没用的。

#16


cooljjyy说得很对,答案是肯定的。关于这个问题,有多种解决办法,我最常用的一种是:
比如两个头文件是MyA.h和MyB.h那么在头文件的最开始添加宏
#ifndef __MYA_H__
#define __MYA_H__
在文件的最后,添加宏
#endif

#17


不应该相互包含
假设a.h定义了类CA,需要引用类CB
而b.h定义了类CB,需要引用类CA
则应该用前向引用的方法解决,即先引入类名,不定义实体。

a.h

class CB;
class CA{
  ...
  CB *m_pB;
}

a.cpp
#include "a.h"
#include "b.h"

b.h
class CA;
class CB{
  ...
  CA *m_pA;
}

b.cpp
#include "a.h"
#include "b.h"

#18


楼上的兄弟,看来你是从来没有用过相互包含或者没有遇到这种情况。有些大的项目,经常要相互引用,如果按照你的方法,虽然可行,但是如果我有5个以上的定义在不同头文件的类需要相互引用,那么在引用之前都要先声明一遍,这个也太费事了。
MS其实早就考虑了这种情况,因此提供了多种方法来解决这种问题。cooljjyy和我提供的只是众多方法的一种。如果你仔细观察由VC自动生成的头文件,那如天书一般的条件宏其实就是为了避免在相互引用的时候重复编译。

#19


加一个 #program once 试一试

#20


to demetry(大松)

我虽然做的大项目不多(我也确实不知道何谓参与过大项目,代码总量超过100万?自己编码超过10万?),但确实碰到过相互包含的情况。况且这个问题似乎与此无关,这是个OOP编程所必然碰到的情况。JAVA VB.NET C#等较新的语言由于只能声明引用而没有此类问题,而C++和Object Pascal(Delphi)的解决方案是前向引用或前置声明(forward declaration),即先引入类名,不定义实体。上面我的方法在C++中显得极端了一些,但实际上这就是模拟了java等语言的方法。我经常用的方法是

a.h

class CB;
class CA{
  ...
  CB *m_pB;
}

a.cpp
#include "a.h"
#include "b.h"

b.h
#include"a.h"
class CB{
  ...
  CA m_A;
}

b.cpp
#include "b.h"

如果你有五个以上的类,我的感觉是循环引用并不会太多。此时取出某一两个类做前置声明即可。如果你设计的类的循环引用关系呈网状一样相互缠绕,恕我直言,你的设计逻辑有问题。这一点你可以看看VC Delphi的源码检验一下。

搂主所碰到的错误与宏定义没有直接关系。如下例

a.h
#include "b.h"
class CA{
  ...
  CB m_B;
}

a.cpp
#include "a.h"

b.h
#include"a.h"
class CB{
  ...
  CA m_A;
}

b.cpp
#include "b.h"

即使你把宏定义与前向引用都加上也无济于事,代码必然出错,因为A类不知道B类的大小,B类不知道A类的大小,编译出错也就理所当然了。

此外天书一般的条件宏是为了避免多次引用,而不是仅仅避免循环引用
如 b.h 引用a.h,c.h 引用b.h,为了避免 c.h引用两次a.h,应该给a.h加上"天书一般的条件宏"

为什么条件宏像天书一般呢?
这点我想请demetry(大松)解答一下

#21


to:zwvista,对不起,我上一个帖子的语气重了一些。不过我只是在讨论技术,决无攻击你的意思。
1。那一串天书一般的条件宏,是Developers Studio的application wizard自动在每个有它生成的头文件加上去的卫式语句。该语句由两部分组成,第一部分是头文件的名称,第二部分是一个很长的十六进制数,是全局唯一性标识符GUID。这个数字是用当前时间和LAN网卡的网络地址建立的。如果没有网卡,则生成一般唯一性标识符UUID。GUID可以保证唯一性,而UUID正在机器上是唯一的。使用GUID,可以保证卫值得独立性。
2。在大型项目中,项目结构有系统架构师否则设计。因此每个程序员一般否则几个模块的编写,虽然有约定好的接口,但是仍然不可避免地出现各种各样的问题。就像一个头文件被多次引用这个问题,一个开发组都公用一些公共的头文件,因此多次重复引用是容易出现的。如果按照你说的方法,那么前提是所有的类都由你自己创建,你知道每一个类的情况。而实际上这在大型工程项目中是不可能的。你可以观察一下MS提供的各种头文件和CPP文件,是不是都按照你所说的方法来引用头文件。
希望我们以后多交流

#22


感谢demetry(大松) 关于宏的解答,长知识了。
这么说来,我确实没有参与过系统架构师主持的大项目。

不过我找到了mfc源码中大量forward declaration的例子
在<afxwin.h>的开始部分,有以下声明

class CSize;
class CPoint;
class CRect;

//CObject
//CException
//CSimpleException
class CResourceException;// Win resource failure exception
class CUserException;    // Message Box alert and stop operation

class CGdiObject;            // CDC drawing tool
class CPen;              // a pen / HPEN wrapper
class CBrush;            // a brush / HBRUSH wrapper
class CFont;             // a font / HFONT wrapper
class CBitmap;           // a bitmap / HBITMAP wrapper
class CPalette;          // a palette / HPALLETE wrapper
class CRgn;              // a region / HRGN wrapper

class CDC;                   // a Display Context / HDC wrapper
class CClientDC;         // CDC for client of window
class CWindowDC;         // CDC for entire window
class CPaintDC;          // embeddable BeginPaint struct helper

class CMenu;                 // a menu / HMENU wrapper

class CCmdTarget;            // a target for user commands
class CWnd;                 // a window / HWND wrapper
class CDialog;          // a dialog


// standard windows controls
class CStatic;          // Static control
class CButton;          // Button control
class CListBox;         // ListBox control
class CCheckListBox;// special listbox with checks
class CComboBox;        // ComboBox control
class CEdit;            // Edit control
class CScrollBar;       // ScrollBar control

// frame windows
class CFrameWnd;        // standard SDI frame
class CMDIFrameWnd; // standard MDI frame
class CMDIChildWnd; // standard MDI child
class CMiniFrameWnd;// half-height caption frame wnd

// views on a document
class CView;            // a view on a document
class CScrollView;  // a scrolling view

class CWinThread;           // thread base class
class CWinApp;          // application base class

class CDocTemplate;         // template for document creation
class CSingleDocTemplate;// SDI support
class CMultiDocTemplate; // MDI support

class CDocument;            // main document abstraction

// Helper classes
class CCmdUI;           // Menu/button enabling
class CDataExchange;    // Data exchange and validation context
class CCommandLineInfo; // CommandLine parsing helper

由此我感觉在引用之前先声明一遍,好像并不费事

再请demetry(大松)找一下mfc源码中a.h引用b.h, b.h引用a.h的例子

#23


Up

#1


可以可以,为什么不可以呢!

你说说理由。

#2


多数情况会产生重复定义的错误,尽量不这么用。

#3


同意,你最好在所用的*.cpp中#include "*.h"

#4


其实*.h和*.cpp是一样的,不过是为了方便,才有两个不同类型的文件的。

#5


我是在两个类中都要用到另一个,
而都在.h文件利用到,编译时出错。
比如:你在CXDoc.h中包含#include "CXView.h"
就会看到错误。

#6


看看你的程序逻辑,能不能重新设计一下,最好不要这样用

#7


有些东西不是逻辑的问题,这种情况有些时候是一定会出现的
解决的办法简单不过
#pragma once
include文件中加上这一行随便怎么包含吧

#8


应该说不允许
c和c++是静态类型检查的语言,也就是说,在编译时,所有的类型都必须确定,然后分配内存。而你的h文件,就是类型声明文件,告诉编译程序你定义了什么类型。而在一个h文件中包含的其他h文件,也就是你在这个h文件中所定义的类型(也许是class)中包含有其他的类型,这些信息编译程序都必须在编译是知道。如果你的两个h文件相互包含,那么在编译是,编译程序就无法确定每个类型的大小了,因为互为依赖。

#9


看来我看的书还是不够,我会继续努力的,以好和大家讨论学习。

#10


这种问题在考试时才会碰到吧!

#11


我觉得完全可以阿,

#12


你可以将两个.h 文件写在一块啊:)
我很讨厌互相包含这种东东
我觉得这是逻辑中的缺陷

#13


不行

#14


可以!
但一定要有存在标识!
例如:a。h
#if !defined(唯一标识该文件的字符串)
  内容
#endif

#15


还在讨论啊,这根本不是什么逻辑的关系,假如有这样一个例子,两个类A,B需要互相调用,最简单的方法就是互相包含对方的头文件,这并没有错,除非你要把A,B写在一个文件中,或者使用interface这样的东东。
互相包含是允许的,为了防止编译时的错误,所以有#pragma once这样的声明,无论头文件被包含多少次,都只编译1次,只要在头文件中加上这一行就可以随意互相包含了,其实在一定的情况下互相包含可以让你的工作更简单,什么东西都是视情况而定,就像goto语句,很多书上已经把它扁得一文不值了,但是假如遇到内联汇编的时候,goto也可以做一些巧妙的工作,看你懂不懂得灵活运用了。所以不要认为什么东西没有用而厌恶它,什么东西都是有用的,只有你不懂的时候它才是没用的。

#16


cooljjyy说得很对,答案是肯定的。关于这个问题,有多种解决办法,我最常用的一种是:
比如两个头文件是MyA.h和MyB.h那么在头文件的最开始添加宏
#ifndef __MYA_H__
#define __MYA_H__
在文件的最后,添加宏
#endif

#17


不应该相互包含
假设a.h定义了类CA,需要引用类CB
而b.h定义了类CB,需要引用类CA
则应该用前向引用的方法解决,即先引入类名,不定义实体。

a.h

class CB;
class CA{
  ...
  CB *m_pB;
}

a.cpp
#include "a.h"
#include "b.h"

b.h
class CA;
class CB{
  ...
  CA *m_pA;
}

b.cpp
#include "a.h"
#include "b.h"

#18


楼上的兄弟,看来你是从来没有用过相互包含或者没有遇到这种情况。有些大的项目,经常要相互引用,如果按照你的方法,虽然可行,但是如果我有5个以上的定义在不同头文件的类需要相互引用,那么在引用之前都要先声明一遍,这个也太费事了。
MS其实早就考虑了这种情况,因此提供了多种方法来解决这种问题。cooljjyy和我提供的只是众多方法的一种。如果你仔细观察由VC自动生成的头文件,那如天书一般的条件宏其实就是为了避免在相互引用的时候重复编译。

#19


加一个 #program once 试一试

#20


to demetry(大松)

我虽然做的大项目不多(我也确实不知道何谓参与过大项目,代码总量超过100万?自己编码超过10万?),但确实碰到过相互包含的情况。况且这个问题似乎与此无关,这是个OOP编程所必然碰到的情况。JAVA VB.NET C#等较新的语言由于只能声明引用而没有此类问题,而C++和Object Pascal(Delphi)的解决方案是前向引用或前置声明(forward declaration),即先引入类名,不定义实体。上面我的方法在C++中显得极端了一些,但实际上这就是模拟了java等语言的方法。我经常用的方法是

a.h

class CB;
class CA{
  ...
  CB *m_pB;
}

a.cpp
#include "a.h"
#include "b.h"

b.h
#include"a.h"
class CB{
  ...
  CA m_A;
}

b.cpp
#include "b.h"

如果你有五个以上的类,我的感觉是循环引用并不会太多。此时取出某一两个类做前置声明即可。如果你设计的类的循环引用关系呈网状一样相互缠绕,恕我直言,你的设计逻辑有问题。这一点你可以看看VC Delphi的源码检验一下。

搂主所碰到的错误与宏定义没有直接关系。如下例

a.h
#include "b.h"
class CA{
  ...
  CB m_B;
}

a.cpp
#include "a.h"

b.h
#include"a.h"
class CB{
  ...
  CA m_A;
}

b.cpp
#include "b.h"

即使你把宏定义与前向引用都加上也无济于事,代码必然出错,因为A类不知道B类的大小,B类不知道A类的大小,编译出错也就理所当然了。

此外天书一般的条件宏是为了避免多次引用,而不是仅仅避免循环引用
如 b.h 引用a.h,c.h 引用b.h,为了避免 c.h引用两次a.h,应该给a.h加上"天书一般的条件宏"

为什么条件宏像天书一般呢?
这点我想请demetry(大松)解答一下

#21


to:zwvista,对不起,我上一个帖子的语气重了一些。不过我只是在讨论技术,决无攻击你的意思。
1。那一串天书一般的条件宏,是Developers Studio的application wizard自动在每个有它生成的头文件加上去的卫式语句。该语句由两部分组成,第一部分是头文件的名称,第二部分是一个很长的十六进制数,是全局唯一性标识符GUID。这个数字是用当前时间和LAN网卡的网络地址建立的。如果没有网卡,则生成一般唯一性标识符UUID。GUID可以保证唯一性,而UUID正在机器上是唯一的。使用GUID,可以保证卫值得独立性。
2。在大型项目中,项目结构有系统架构师否则设计。因此每个程序员一般否则几个模块的编写,虽然有约定好的接口,但是仍然不可避免地出现各种各样的问题。就像一个头文件被多次引用这个问题,一个开发组都公用一些公共的头文件,因此多次重复引用是容易出现的。如果按照你说的方法,那么前提是所有的类都由你自己创建,你知道每一个类的情况。而实际上这在大型工程项目中是不可能的。你可以观察一下MS提供的各种头文件和CPP文件,是不是都按照你所说的方法来引用头文件。
希望我们以后多交流

#22


感谢demetry(大松) 关于宏的解答,长知识了。
这么说来,我确实没有参与过系统架构师主持的大项目。

不过我找到了mfc源码中大量forward declaration的例子
在<afxwin.h>的开始部分,有以下声明

class CSize;
class CPoint;
class CRect;

//CObject
//CException
//CSimpleException
class CResourceException;// Win resource failure exception
class CUserException;    // Message Box alert and stop operation

class CGdiObject;            // CDC drawing tool
class CPen;              // a pen / HPEN wrapper
class CBrush;            // a brush / HBRUSH wrapper
class CFont;             // a font / HFONT wrapper
class CBitmap;           // a bitmap / HBITMAP wrapper
class CPalette;          // a palette / HPALLETE wrapper
class CRgn;              // a region / HRGN wrapper

class CDC;                   // a Display Context / HDC wrapper
class CClientDC;         // CDC for client of window
class CWindowDC;         // CDC for entire window
class CPaintDC;          // embeddable BeginPaint struct helper

class CMenu;                 // a menu / HMENU wrapper

class CCmdTarget;            // a target for user commands
class CWnd;                 // a window / HWND wrapper
class CDialog;          // a dialog


// standard windows controls
class CStatic;          // Static control
class CButton;          // Button control
class CListBox;         // ListBox control
class CCheckListBox;// special listbox with checks
class CComboBox;        // ComboBox control
class CEdit;            // Edit control
class CScrollBar;       // ScrollBar control

// frame windows
class CFrameWnd;        // standard SDI frame
class CMDIFrameWnd; // standard MDI frame
class CMDIChildWnd; // standard MDI child
class CMiniFrameWnd;// half-height caption frame wnd

// views on a document
class CView;            // a view on a document
class CScrollView;  // a scrolling view

class CWinThread;           // thread base class
class CWinApp;          // application base class

class CDocTemplate;         // template for document creation
class CSingleDocTemplate;// SDI support
class CMultiDocTemplate; // MDI support

class CDocument;            // main document abstraction

// Helper classes
class CCmdUI;           // Menu/button enabling
class CDataExchange;    // Data exchange and validation context
class CCommandLineInfo; // CommandLine parsing helper

由此我感觉在引用之前先声明一遍,好像并不费事

再请demetry(大松)找一下mfc源码中a.h引用b.h, b.h引用a.h的例子

#23


Up