关闭

C++头文件包含问题-Include和class **

732人阅读 评论(0) 收藏 举报
在一个类的头文件中声明了一个类的成员,这个成员是一个指向另一个类的对象的指针则必须在这个头文件中用class class_name;这样的形式作一声吗?为什么?虽然我知道它的大概意思,但一直没找到权威的解释,请赐教。
如下,在类A的头文件中声明了一个成员,这个成员是指向B的对象的指针,则在类A的头文件的前面必需包含class B;这样的声明吗?为什么呢?在哪些情形下必须这么做?
// 类A的头文件
class B;
class A
{
...
protected:
  B *m_pB;
...
}

另外请问可以在同样的地方声明类的对象而不是指阵吗?如何解释?

class B;
是前置声明,就是在没有定义class B;的情况在一个使用个类。

应该是必需。
class B;只不过是声明,告诉后面的有这样一个类,但是在这个类实现之前,只能定义该类的指针,不能定义对象,只有在这个类实现了之后,才能定义该类的对象!

一个类在没有实现的时候是不能创建对象的,

这个叫前向声明(foreward declaration)。如果你不这么声明一下,那编译器编到这个地方时怎么能知道B是一个class呢?它不知道B是个class,那它又怎么知道m_pB是个什么东西呢?于是它就编不下去了...
    有了那个声明,一切就不同了。这个时候编译器就知道了m_pB是一个指针,而且是指向一个类型为B的对象的指针。由于所有类型的指针大小都是一样的,所以编译器无需知道B的定义(也就是B里面有些什么成员变量和成员函数)就可以为m_pB分配存储空间。同样,由于声明一个引用不会引起存储空间的分配,所以如果把例子中A的声明里的B*改成B&,也只需要知道B是一个class就可以了。当然,在A早晚肯定是要用到B里的成员变量或成员函数的(不然你就不需要m_pB这个东西了),在那个时候时编译器必须能看到完整的B的定义。

class B;
指出名字B是一个类类型。
B *m_pB;
声明一个类型为B*的指针。
B m_B;
这样的东西声明一个类型为B的对象。
在class A中声明成员数据是要决定A的内存布局的。
我们声明一个B,或B*,都需要首先声明名字B是什么。
而两者的不同点主要在于:
B m_B;//需要完整的B声明
B *m_pB;//只需要声明名字B
这是因为,为了决定A的内存布局,我们需要首先决定m_B或m_pB的内存布局。
m_B一定要完整的B声明已知。
而虽然指针m_pB本身有类型,但其内存布局并不与B的内存布局相关,所以只需要决定B的名字即可。

使用前置声明的好处是,不需要再编译这个文件时再处理包含的头文件,这样可以节省编译时间。
不过在使用前置声明时该文件里只能存在声明的类的指针或引用,也就是说不需要为这个类的对象在编译期分配内存,这样就不需要了解对象的具体定义了。

如果在前面包含的头文件中已经有这个类的声明呢?还必须要还是不要或者可要可不要这种前向声明?

提供前向声明的目的就是要让编译器知道怎么解释B这个符号。如果已包含的头文件里已经有了B这个类的声明,那当然就不需要再声明一次了。不过,有时候你不一定能确定头文件里是否包含了你需要的声明,那就再声明一次好了。反正C++允许多次声明。另外,出现前向声明一般都是因为两个类需要互相包含,而这种情况通常可以通过重新设计类之间的关系来避免。

出现前向声明一般都是因为两个类需要互相包含。
这句最正确,也是必须用前向声明的情况,别的情况可用头文件。

class B只是声明,没有定义
而class A 声明 定义了!

类的前置声明,在类定义前使用该类是使用

一般我解决互相包含头文件的问题,都不用向前声明,而是引出一个全局变量,指向包含本类的类。
比如说class A的头文件A.h包含class B的头文件B.h,
而b.cpp要用到一个贯穿始终的A对象(如果只是局部需要使用class A,在b.cpp里包含A.h就行了),
我就在b.cpp里放一个A * g_a = NULL;,
然后在创建B对象时把对象A的指针传进去就行了。
有时候类之间的关系太复杂的时候我最讨厌向前声明了。

声明啊。c也有这样的东西。

http://bbs.csdn.net/topics/60315139

---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

在一些大的工程中,可能会包含几十个基础类,免不了之间会互相引用(不满足继承关系,而是组合关系)。也就是需要互相声明。好了,这时候会带来一些混乱。如果处理得不好,会搞得一团糟,根据我的经验,简单谈谈自已的处理办法:

编码时,我们一般会尽量避免include头文件,而是采用声明class XXX。 但有时候还是必须用Include头文件,那么,两者的划分在于什么呢?

应该是很明确的,但书上好像都少有提及。

首先:
我们要明白为什么要用声明取代头文件包含:对了,是为了避免无必要的重编译(在头文件发生变更时)。 工程较大,低速机,或基础类经常变更(不合理的设计吧),编译速度还是会在意的, 另外,更为重要的是,采用声明可降低代码(class)之间的藕合度,这也是面向对象设计的一大原则。

二:一般原则:
a.头文件中尽量少include,如果可以简单申明class clsOld;解决,那最好。减少没有必要的include;
b.实现文件中也要尽量少include,不要include没有用到的头文件。

三:那什么时候可以只是简单声明class clsOld呢?
简单的说:不需要知道clsOld的内存布局的用法都可以(静态成员除外),也就是讲如果是指针或引用方式的 都行。
比如:
clsOld * m_pOld; //指针占4个字节长
clsOld & test(clsOld * pOld) {return *pOld};
一切OK。

四:什么时候不能简单声明class clsOld,必须include呢?
不满足三的情况下:
比如:
clsOld m_Objold; //不知道占据大小,必须要通过它的具体声明来计算
原因很简单,想想你要计算sizeof(classNew),但连clsOld的size都不知道,编译器显然会无能为力。

特殊情况:
int test() { return clsOld::m_sInt;}
静态成员调用,想来应该是不需要知道内存布局的,但因为需要知道m_sInt是属于clsOld命名空间 的,如果只声明class xxx显然是不足以说明的,所以必须包含头文件。

综上所述,我有以下几点建议:
1:如果有共同相关依赖(必须include)的类,比如A,B都依赖D可以放在一起,然后直接Include “d”类的使用者只需关心与本类暴露出的相关类型,内部用到的类型不用去管(不用自已去include d)。这样 给出的class,调用者才更好用(不用去看代码查找,是不是还需要包含其它头文件)。

2:如果A类依赖D B类不依赖D,可以把它们分开两个头文件。各自Include。这样可避免当D发生变化时, 避免不必要重编译。

3:类中尽量采用指针或引用方式调用其它类,这样就可以只声明class xxx了。并且这也符合资源最优 利用,更利于使用多态。

http://blog.csdn.net/wuyu1125/article/details/7981571

0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:185714次
    • 积分:2802
    • 等级:
    • 排名:第12804名
    • 原创:82篇
    • 转载:110篇
    • 译文:0篇
    • 评论:6条
    文章分类
    最新评论