C++头文件相互包含的问题

时间:2022-07-18 16:49:48

昨天发的一个帖子: http://topic.csdn.net/u/20110912/16/6b625bc9-0b85-421e-bde2-01bc5f7cbf76.html
(暂时还存在问题,仍未结贴)

下面叙述详细的问题:

遇到这样一个情况,下面是一个简化的说明:

在A.h中定义了一个类模版,并且在A.h中需要#include "B.h"(即,需要知道B的定义)
而在B.h中又使用了A.h中定义的模版,因此也需要#include "A.h"(即,又需要知道A的定义)

问题1:这段代码应该是不对的,但在gcc3.3.4上没有报错,而在gcc4.1.2上报错:
error: incomplete type 'CXXX' used in nested name specifier

问题2:通常相互依赖的2个类,把有依赖项的函数的具体实现都放入CPP中,类似的问题就解决了。但是A.h中使用的是类模版,大部分编译器只支持 inclusion compliation model,即,不支持分别编译模型。所以,将依赖的代码放在CPP中就不可行了。


有些理解不对的地方,请大大们指教!分数不多,见谅

38 个解决方案

#1


头文件相互包含,不但要用到#include,而且还要分别声明要用到的类。
比如A.h中,添加#include"B.h",而且要添加class B;B.h中也是一样,否则就报错。
编译器不支持模板分离编译,这也是没有办法的事情,要用类模板,就把实现也写在头文件中吧。

#2


1.相互引用不能用对象,只能用引用和指针

2.大部分编译器不支持分离编译

#3


引用 2 楼 jackyjkchen 的回复:
1.相互引用不能用对象,只能用引用和指针

2.大部分编译器不支持分离编译


++

补充一下,解决起来,可以利用CPP文件,就是.h里面只有声明(互相引用的就只用指针引用形式,这样就不需要include了,只要前置声明一下。在cpp里面再include即可)

模板永远不会支持export关键字了。

#4


呵呵 来看看这位老大的解释

#5


引用 1 楼 kevin_perkins 的回复:
头文件相互包含,不但要用到#include,而且还要分别声明要用到的类。
比如A.h中,添加#include"B.h",而且要添加class B;B.h中也是一样,否则就报错。
编译器不支持模板分离编译,这也是没有办法的事情,要用类模板,就把实现也写在头文件中吧。

A.h中的类模版在B.h中怎么声明呢?
// A.h中的类模版定义
template <class CR,class CS>
class CA : public CBase
{
//...

#6


引用 3 楼 healer_kx 的回复:
引用 2 楼 jackyjkchen 的回复:

1.相互引用不能用对象,只能用引用和指针

2.大部分编译器不支持分离编译


++

补充一下, 解决起来,可以利用CPP文件,就是.h里面只有声明(互相引用的就只用指针引用形式,这样就不需要include了,只要前置声明一下。在cpp里面再include即可)

模板永远不会支持export关键字了。

“解决起来,可以利用CPP文件,就是.h里面只有声明”
这个方法我知道,但是在A.h中必须要知道B.h的定义,只有前向声明是不行的。
比如,在A.h中,调用
B::static_funB();// 在A.h中调用B.h中的静态函数,此时在A.h中必须要知道B类型的完整定义

#7


引用 2 楼 jackyjkchen 的回复:
1.相互引用不能用对象,只能用引用和指针

2.大部分编译器不支持分离编译

经典的pimpl手法我知道,但是对我的问题又不太一样。

#8


模板的话不支持分开编译,
要把头文件源文件一起包含,如:
#include "test.h"
#include "test.cpp"

#9


把void funB()的实现放到b.cpp里去。

#10


引用 3 楼 healer_kx 的回复:
引用 2 楼 jackyjkchen 的回复:

1.相互引用不能用对象,只能用引用和指针

2.大部分编译器不支持分离编译


++

补充一下,解决起来,可以利用CPP文件,就是.h里面只有声明(互相引用的就只用指针引用形式,这样就不需要include了,只要前置声明一下。在cpp里面再include即可)

模板永远不会支持export关键字了。

或者说,您提出的解决方法,如何解决问题2呢?即,头文件A.h中定义的是类模版。

#11


引用 9 楼 luciferisnotsatan 的回复:
把void funB()的实现放到b.cpp里去。

我试过,问题是别的类在使用模版类的时候怎么办,如果#include "B.cpp",可能会出现重复inclusion问题(重复定义问题)。

#12


引用 11 楼 return_zero 的回复:
引用 9 楼 luciferisnotsatan 的回复:

把void funB()的实现放到b.cpp里去。

我试过,问题是别的类在使用模版类的时候怎么办,如果#include "B.cpp",可能会出现重复inclusion问题(重复定义问题)。

A.h里没用到B.h的内容。
把#include "B.h"的代码放到A.h最后(#endif上面)。

#13


如果是模版,就把A.cpp里的实现全写到A.h里。把A.cpp删了。

#14


我习惯把他们都放在同一个文件上,前置声明一下就行了

#15


@luciferisnotsatan 12L 谢谢
您说的“A.h里没用到B.h的内容”,鄙人不太理解。
在A.h中必须要知道B.h的定义,只有前向声明是不行的。
比如,在A.h中,调用
B::static_funB();// 在A.h中调用B.h中的静态函数,此时在A.h中必须要知道B类型的完整定义。


#16


引用 13 楼 luciferisnotsatan 的回复:
如果是模版,就把A.cpp里的实现全写到A.h里。把A.cpp删了。

目前就是这样的,模版类是在A.h中定义和实现的,A.cpp中实现了一些其他的函数(说明一下,A.h中除了定义了一个模版类之外,还定义了几个普通的类)。

如果将A.cpp删除,本质上还是没有解决头文件 circular inclusion 的问题,即两个相互依赖的类相互包含的问题。

鄙人写了一个测试的例子:非模版的两个相互包含的类,可以通过把依赖的实现代码移到CPP中得以解决,但是,对于含有模版的两个相互包含的类,就无可奈何了。

下面的代码是——不含模版类时的解决方法:

// A.h
#ifndef _A_H_
#define _A_H_

#include "B.h"

class A
{
public:
static A& getTheApp();
void funA();

private:
static A* theApp;


};
#endif

/////////////////////////////////////////////
// A.cpp

#include "stdafx.h"
#include "A.h"


A* A::theApp = NULL;

A& A::getTheApp()
{
 if (theApp == NULL) {
 theApp = new A();
 }

 return *theApp;
}

void A::funA() {
printf("funA...\n");

B b;
printf("i_b = %d\n", b.i_b);
}
////////////////////////////////////////////////

// B.h
#ifndef _B_H_
#define _B_H_

#include "A.h"

class B
{
public:
B(){i_b = 100;}
void funB();
int i_b;

};
#endif

/////////////////////////////////////

// B.cpp

#include "stdafx.h"
#include "B.h"

void B::funB()
{
printf("funB...\n");
A::getTheApp().funA();
}

//////////////////////////////

// 主程序

#include "stdafx.h"
#include "A.h"
#include "B.h"


int main(int argc, _TCHAR* argv[])
{
B b;
b.funB();

return 0;
}


#17


#ifndef _A_H_
#define _A_H_



class A
{
public:
    static A& getTheApp();
    void funA();

private:
    static A* theApp;
    

};
#include "B.h"
#endif

这样,你就可以把funB放到B.h里去了。
但如果这是都是自己写的代码,建议还是自己整理下思路。看看能不能简化,别做出这么复杂的东西。

#18


引用 12 楼 luciferisnotsatan 的回复:
引用 11 楼 return_zero 的回复:

引用 9 楼 luciferisnotsatan 的回复:

把void funB()的实现放到b.cpp里去。

我试过,问题是别的类在使用模版类的时候怎么办,如果#include "B.cpp",可能会出现重复inclusion问题(重复定义问题)。

A.h里没用到B.h的内容。
把#include "B.h"的代码放到A……

++

#19


ifndef
define
endif

#20


模板不支持分离编译,所以模板没有CPP文件一说。

#21


谢谢,可能是我没有说清楚,我是在维护编译的代码,现在出现了这样的问题(见顶端问题1)。

我现在把问题再描述一下:

// A.h
#include "B.h"// 要知道B的定义

template <...>// A.h 中的模版类 CA
class CA
{
public:
void funA()
{
B:funB();// 这里需要知道 B 的完整定义
}

};

// B.h
#include "A.h"// 要包含模版类的定义
class B
{
typedef CA<...> CAlias;// 这里需要使用模版类 CA
CAlias * m_b;

//...

static funB();
};


相互依赖的问题就出现了。

引用 17 楼 luciferisnotsatan 的回复:
#ifndef _A_H_
#define _A_H_



class A
{
public:
    static A&amp; getTheApp();
    void funA();

private:
    static A* theApp;
    

};
#include "B.h"
#endif

这样,你就可以把funB放到B.h里去……

#22


引用 20 楼 dahuaixiaohuai 的回复:
模板不支持分离编译,所以模板没有CPP文件一说。

A.h中定义的模版类和实现,A.cpp中是另外一些类的实现,见16L

#23


你昨天可没说那是个模板类。

#24


引用 23 楼 qq120848369 的回复:
你昨天可没说那是个模板类。

感谢 蜡笔小新 qq120848369的关注!
不知道当初设计者的意图是什么,类之间耦合的比较厉害,而且奇怪为什么gcc 3.3.4没有报错。
PS:第一个帖子还有一个问题没有回答。

#25


学习下,拿分

#26


这个好难懂呀

#27


支持帖子 别沉默了哦

#28


引用 27 楼 wb443010519 的回复:
支持帖子 别沉默了哦

谢谢!
现在通过一种方法编译通过了,正在测试程序是否正确。同时仍然希望有经验的朋友给些建议。

#29


模板支持三种类型
1.全部放进头文件.h里称为置入型模型 可以泛化任何类型
2.声明放在头文件 定义放在cpp中 又称显示实例化 编译时会去编译cpp 要使它能找到编译的类型  必须手动告知编译器要泛化的类型  即
 template class TemplateClass<T>; 

3.export 声明手动导出 称为分离编译模型 但是目前编译器还不支持 至少vs

如果LZ只是要用模板这个类型可以用前置声明 但是只限用类型 其他情况必须包含头文件

#30


路过求分+学习

#31


引用 29 楼 stendson 的回复:
模板支持三种类型
1.全部放进头文件.h里称为置入型模型 可以泛化任何类型
2.声明放在头文件 定义放在cpp中 又称显示实例化 编译时会去编译cpp 要使它能找到编译的类型  必须手动告知编译器要泛化的类型  即
 template class TemplateClass<T>; 
  
3.export 声明手动导出 称为分离编译模型 但是目前编译器还不支持……

"如果LZ只是要用模板这个类型可以用前置声明 但是只限用类型 其他情况必须包含头文件"
格式如何写?此方式好像试过会提示识别不了<符号
好像在搜过的相关文章上看到,在别的文件中使用类模版,必须包含这个模版的定义,我去试下

#32


#ifndef
#endif

#33


Vc6.0下。。亲试。。可编译通过。。。

#34


在一个头文件中用class声明一下另一个头文件中的类,在另一个头文件中include。一般不建议两个类这般联系,应该好好设计下类层次结构。

#35


学习了。。。

#36


#ifndef AAAAA
#define AAAAA

#include "B.h"

class A{...};


#endif


#37


前置声明 class T;只是告知编译器 存在这个类型 主要是用于模板 显示实例化
template class TemplateClass<T>;
只要将这句加到T的.h中就行了 但是这样就只能实例化T 不能实例化其他类型

#38


另外给你推荐一本书 c++ template 
http://www.docin.com/p-92736755.html
第六章 好好看吧

#1


头文件相互包含,不但要用到#include,而且还要分别声明要用到的类。
比如A.h中,添加#include"B.h",而且要添加class B;B.h中也是一样,否则就报错。
编译器不支持模板分离编译,这也是没有办法的事情,要用类模板,就把实现也写在头文件中吧。

#2


1.相互引用不能用对象,只能用引用和指针

2.大部分编译器不支持分离编译

#3


引用 2 楼 jackyjkchen 的回复:
1.相互引用不能用对象,只能用引用和指针

2.大部分编译器不支持分离编译


++

补充一下,解决起来,可以利用CPP文件,就是.h里面只有声明(互相引用的就只用指针引用形式,这样就不需要include了,只要前置声明一下。在cpp里面再include即可)

模板永远不会支持export关键字了。

#4


呵呵 来看看这位老大的解释

#5


引用 1 楼 kevin_perkins 的回复:
头文件相互包含,不但要用到#include,而且还要分别声明要用到的类。
比如A.h中,添加#include"B.h",而且要添加class B;B.h中也是一样,否则就报错。
编译器不支持模板分离编译,这也是没有办法的事情,要用类模板,就把实现也写在头文件中吧。

A.h中的类模版在B.h中怎么声明呢?
// A.h中的类模版定义
template <class CR,class CS>
class CA : public CBase
{
//...

#6


引用 3 楼 healer_kx 的回复:
引用 2 楼 jackyjkchen 的回复:

1.相互引用不能用对象,只能用引用和指针

2.大部分编译器不支持分离编译


++

补充一下, 解决起来,可以利用CPP文件,就是.h里面只有声明(互相引用的就只用指针引用形式,这样就不需要include了,只要前置声明一下。在cpp里面再include即可)

模板永远不会支持export关键字了。

“解决起来,可以利用CPP文件,就是.h里面只有声明”
这个方法我知道,但是在A.h中必须要知道B.h的定义,只有前向声明是不行的。
比如,在A.h中,调用
B::static_funB();// 在A.h中调用B.h中的静态函数,此时在A.h中必须要知道B类型的完整定义

#7


引用 2 楼 jackyjkchen 的回复:
1.相互引用不能用对象,只能用引用和指针

2.大部分编译器不支持分离编译

经典的pimpl手法我知道,但是对我的问题又不太一样。

#8


模板的话不支持分开编译,
要把头文件源文件一起包含,如:
#include "test.h"
#include "test.cpp"

#9


把void funB()的实现放到b.cpp里去。

#10


引用 3 楼 healer_kx 的回复:
引用 2 楼 jackyjkchen 的回复:

1.相互引用不能用对象,只能用引用和指针

2.大部分编译器不支持分离编译


++

补充一下,解决起来,可以利用CPP文件,就是.h里面只有声明(互相引用的就只用指针引用形式,这样就不需要include了,只要前置声明一下。在cpp里面再include即可)

模板永远不会支持export关键字了。

或者说,您提出的解决方法,如何解决问题2呢?即,头文件A.h中定义的是类模版。

#11


引用 9 楼 luciferisnotsatan 的回复:
把void funB()的实现放到b.cpp里去。

我试过,问题是别的类在使用模版类的时候怎么办,如果#include "B.cpp",可能会出现重复inclusion问题(重复定义问题)。

#12


引用 11 楼 return_zero 的回复:
引用 9 楼 luciferisnotsatan 的回复:

把void funB()的实现放到b.cpp里去。

我试过,问题是别的类在使用模版类的时候怎么办,如果#include "B.cpp",可能会出现重复inclusion问题(重复定义问题)。

A.h里没用到B.h的内容。
把#include "B.h"的代码放到A.h最后(#endif上面)。

#13


如果是模版,就把A.cpp里的实现全写到A.h里。把A.cpp删了。

#14


我习惯把他们都放在同一个文件上,前置声明一下就行了

#15


@luciferisnotsatan 12L 谢谢
您说的“A.h里没用到B.h的内容”,鄙人不太理解。
在A.h中必须要知道B.h的定义,只有前向声明是不行的。
比如,在A.h中,调用
B::static_funB();// 在A.h中调用B.h中的静态函数,此时在A.h中必须要知道B类型的完整定义。


#16


引用 13 楼 luciferisnotsatan 的回复:
如果是模版,就把A.cpp里的实现全写到A.h里。把A.cpp删了。

目前就是这样的,模版类是在A.h中定义和实现的,A.cpp中实现了一些其他的函数(说明一下,A.h中除了定义了一个模版类之外,还定义了几个普通的类)。

如果将A.cpp删除,本质上还是没有解决头文件 circular inclusion 的问题,即两个相互依赖的类相互包含的问题。

鄙人写了一个测试的例子:非模版的两个相互包含的类,可以通过把依赖的实现代码移到CPP中得以解决,但是,对于含有模版的两个相互包含的类,就无可奈何了。

下面的代码是——不含模版类时的解决方法:

// A.h
#ifndef _A_H_
#define _A_H_

#include "B.h"

class A
{
public:
static A& getTheApp();
void funA();

private:
static A* theApp;


};
#endif

/////////////////////////////////////////////
// A.cpp

#include "stdafx.h"
#include "A.h"


A* A::theApp = NULL;

A& A::getTheApp()
{
 if (theApp == NULL) {
 theApp = new A();
 }

 return *theApp;
}

void A::funA() {
printf("funA...\n");

B b;
printf("i_b = %d\n", b.i_b);
}
////////////////////////////////////////////////

// B.h
#ifndef _B_H_
#define _B_H_

#include "A.h"

class B
{
public:
B(){i_b = 100;}
void funB();
int i_b;

};
#endif

/////////////////////////////////////

// B.cpp

#include "stdafx.h"
#include "B.h"

void B::funB()
{
printf("funB...\n");
A::getTheApp().funA();
}

//////////////////////////////

// 主程序

#include "stdafx.h"
#include "A.h"
#include "B.h"


int main(int argc, _TCHAR* argv[])
{
B b;
b.funB();

return 0;
}


#17


#ifndef _A_H_
#define _A_H_



class A
{
public:
    static A& getTheApp();
    void funA();

private:
    static A* theApp;
    

};
#include "B.h"
#endif

这样,你就可以把funB放到B.h里去了。
但如果这是都是自己写的代码,建议还是自己整理下思路。看看能不能简化,别做出这么复杂的东西。

#18


引用 12 楼 luciferisnotsatan 的回复:
引用 11 楼 return_zero 的回复:

引用 9 楼 luciferisnotsatan 的回复:

把void funB()的实现放到b.cpp里去。

我试过,问题是别的类在使用模版类的时候怎么办,如果#include "B.cpp",可能会出现重复inclusion问题(重复定义问题)。

A.h里没用到B.h的内容。
把#include "B.h"的代码放到A……

++

#19


ifndef
define
endif

#20


模板不支持分离编译,所以模板没有CPP文件一说。

#21


谢谢,可能是我没有说清楚,我是在维护编译的代码,现在出现了这样的问题(见顶端问题1)。

我现在把问题再描述一下:

// A.h
#include "B.h"// 要知道B的定义

template <...>// A.h 中的模版类 CA
class CA
{
public:
void funA()
{
B:funB();// 这里需要知道 B 的完整定义
}

};

// B.h
#include "A.h"// 要包含模版类的定义
class B
{
typedef CA<...> CAlias;// 这里需要使用模版类 CA
CAlias * m_b;

//...

static funB();
};


相互依赖的问题就出现了。

引用 17 楼 luciferisnotsatan 的回复:
#ifndef _A_H_
#define _A_H_



class A
{
public:
    static A&amp; getTheApp();
    void funA();

private:
    static A* theApp;
    

};
#include "B.h"
#endif

这样,你就可以把funB放到B.h里去……

#22


引用 20 楼 dahuaixiaohuai 的回复:
模板不支持分离编译,所以模板没有CPP文件一说。

A.h中定义的模版类和实现,A.cpp中是另外一些类的实现,见16L

#23


你昨天可没说那是个模板类。

#24


引用 23 楼 qq120848369 的回复:
你昨天可没说那是个模板类。

感谢 蜡笔小新 qq120848369的关注!
不知道当初设计者的意图是什么,类之间耦合的比较厉害,而且奇怪为什么gcc 3.3.4没有报错。
PS:第一个帖子还有一个问题没有回答。

#25


学习下,拿分

#26


这个好难懂呀

#27


支持帖子 别沉默了哦

#28


引用 27 楼 wb443010519 的回复:
支持帖子 别沉默了哦

谢谢!
现在通过一种方法编译通过了,正在测试程序是否正确。同时仍然希望有经验的朋友给些建议。

#29


模板支持三种类型
1.全部放进头文件.h里称为置入型模型 可以泛化任何类型
2.声明放在头文件 定义放在cpp中 又称显示实例化 编译时会去编译cpp 要使它能找到编译的类型  必须手动告知编译器要泛化的类型  即
 template class TemplateClass<T>; 

3.export 声明手动导出 称为分离编译模型 但是目前编译器还不支持 至少vs

如果LZ只是要用模板这个类型可以用前置声明 但是只限用类型 其他情况必须包含头文件

#30


路过求分+学习

#31


引用 29 楼 stendson 的回复:
模板支持三种类型
1.全部放进头文件.h里称为置入型模型 可以泛化任何类型
2.声明放在头文件 定义放在cpp中 又称显示实例化 编译时会去编译cpp 要使它能找到编译的类型  必须手动告知编译器要泛化的类型  即
 template class TemplateClass<T>; 
  
3.export 声明手动导出 称为分离编译模型 但是目前编译器还不支持……

"如果LZ只是要用模板这个类型可以用前置声明 但是只限用类型 其他情况必须包含头文件"
格式如何写?此方式好像试过会提示识别不了<符号
好像在搜过的相关文章上看到,在别的文件中使用类模版,必须包含这个模版的定义,我去试下

#32


#ifndef
#endif

#33


Vc6.0下。。亲试。。可编译通过。。。

#34


在一个头文件中用class声明一下另一个头文件中的类,在另一个头文件中include。一般不建议两个类这般联系,应该好好设计下类层次结构。

#35


学习了。。。

#36


#ifndef AAAAA
#define AAAAA

#include "B.h"

class A{...};


#endif


#37


前置声明 class T;只是告知编译器 存在这个类型 主要是用于模板 显示实例化
template class TemplateClass<T>;
只要将这句加到T的.h中就行了 但是这样就只能实例化T 不能实例化其他类型

#38


另外给你推荐一本书 c++ template 
http://www.docin.com/p-92736755.html
第六章 好好看吧