c++ 之类的前置声明

作者: 清林 (2 篇文章) 日期: 五月 4, 2010 在 10:02 上午

刚开始学习c++的人都会遇到这样的问题:

定义一个类 class A,这个类里面使用了类B的对象b,然后定义了一个类B,里面也包含了一个类A的对象a,就成了这样:

一编译,就出现了一个互包含的问题了,这时就有人跳出来说,这个问题的解决办法可以这样,在a.h文件中声明类B,然后使用B的指针。

然后,问题就解决了。

但是,有人知道问题是为什么就被解决的吗,也就是说,加了个前置声明为什么就解决了这样的问题。下面,让我来探讨一下这个前置声明。

类的前置声明是有许多的好处的。

我们使用前置声明的一个好处是,从上面看到,当我们在类A使用类B的前置声明时,我们修改类B时,只需要重新编译类B,而不需要重新编译a.h的(当然,在真正使用类B时,必须包含b.h)。

另外一个好处是减小类A的大小,上面的代码没有体现,那么我们来看下:

我们看上面的代码,类B的大小是12(在32位机子上)。

如果我们在类A中包含的是B的对象,那么类A的大小就是12(假设没有其它成员变量和虚函数)。如果包含的是类B的指针*b变量,那么类A的大小就是4,所以这样是可以减少类A的大小的,特别是对于在STL的容器里包含的是类的对象而不是指针的时候,这个就特别有用了。

在前置声明时,我们只能使用的就是类的指针和引用(因为引用也是居于指针的实现的)。

那么,我问你一个问题,为什么我们前置声明时,只能使用类型的指针和引用呢?

如果你回答到:那是因为指针是固定大小,并且可以表示任意的类型,那么可以给你80分了。为什么只有80分,因为还没有完全回答到。

想要更详细的答案,我们看下下面这个类:


我们看下上面定义的这个类A,其中_b变量和get_b()函数是新增加进这个类的。

那么我问你,在增加进_b变量和get_b()成员函数后这个类发生了什么改变,思考一下再回答。

好了,我们来列举这些改变:

第一个改变当然是增加了_b变量和get_b()成员函数;

第二个改变是这个类的大小改变了,原来是4,现在是8。

第三个改变是成员_a的偏移地址改变了,原来相对于类的偏移是0,现在是4了。

上面的改变都是我们显式的、看得到的改变。还有一个隐藏的改变,想想是什么。。。

这个隐藏的改变是类A的默认构造函数和默认拷贝构造函数发生了改变。

由上面的改变可以看到,任何调用类A的成员变量或成员函数的行为都需要改变,因此,我们的a.h需要重新编译。

如果我们的b.h是这样的:

那么我们的b.h也需要重新编译。

如果是这样的:

那么我们的b.h就不需要重新编译。

像我们这样前置声明类A:

class A;

是一种不完整的声明,只要类B中没有执行需要了解类A的大小或者成员的操作,则这样的不完整声明允许声明指向A的指针和引用。

而在前一个代码中的语句

A a;

是需要了解A的大小的,不然是不可能知道如果给类B分配内存大小的,因此不完整的前置声明就不行,必须要包含a.h来获得类A的大小,同时也要重新编译类B。

再回到前面的问题,使用前置声明只允许的声明是指针或引用的一个原因是只要这个声明没有执行需要了解类A的大小或者成员的操作就可以了,所以声明成指针或引用是没有执行需要了解类A的大小或者成员的操作的。

分类: 博客征文专栏, 软件开发工具
标签:

如需了解英特尔软件产品相关的性能和优化选项,请参阅优化注意事项.

 评论 (27)

2010年05月05日 17:19


abvc
第二个代码中不是用到了指针?但代码里好像没有。B b;应该是 B *b吧。
2010年05月05日 18:30


g_cofa
交叉情况,只能用指针或引用吧。
2010年05月05日 18:37


phikaa
很nice,细节是王道~
2010年05月05日 22:47

khler
khler总分:
135
注册用户


分析到位,不错

2010年05月05日 22:57


王老师
低价快速办理统招,自考真实文凭和各类资格认证。有意请联系qq65785746
电话13732251058.王老师
2010年05月05日 23:21


kerwinpc
很好的帖子,但是怎么在第二个图中没有看到指针?
2010年05月06日 01:57


合肥大头装饰
讲得不错,编程中前置声明很重要。
2010年05月06日 02:42


ddr
我大二就是这么做的
2010年05月06日 03:10

yibaini
yibaini总分:
10
注册用户
写的不错 面向对象时 老师讲过。 我觉得还有那个虚函数表也是比较奇妙的 lz可以分析下 最近没有时间 要不可以写一写。
2010年05月06日 03:14


wpzhen
很好
2010年05月06日 07:10


lzonline01
你那个第二段代码应该是指针或者引用类型吧
2010年05月06日 07:12

lzonline01
lzonline01总分:
5
注册用户
你那个第二段代码应该是指针或者引用类型吧
2010年05月06日 09:07

feifei123456
feifei123456总分:
5
注册用户
很好!
2010年05月06日 17:09


ruanmengkai
应该说在类A声明中,里面存的是个B的指针或引用,因为指针在内存中只是表示一个变量或对象的首地址地址,具体怎么解释这个指针,指针所覆 盖的长度是多少,是编译器帮你实现的,所以当你真正需要用到B的时候,编译器已经知道了B对象的大小是多少,这时候,该指针所覆盖的长度也 就被解释出来了。前置声明只是告诉编译器,这个B是个自定义类型。
2010年05月06日 19:22


sb
SB, 还在讲类声明问题,鄙视!!!
2010年05月06日 22:04


tongyong_fly
顶,太好了。
2010年05月06日 22:07


冬雪
Very good!Thanks a lot.
2010年05月07日 00:12


feifei
分析很到位,细节决定成败
2010年05月07日 03:11


桑琰
这个代码 对吗 要先初始化 _b 但是 _b(_a), _b 用 _a 初始化啊 这个时候 _a 还没有初始化吧
2010年05月07日 22:35


zltianhen
写的不错,其实写过类似代码的人,都会遇到嵌套的问题。

2010年05月11日 20:10

alren@126.com
alren@126.com总分:
10
注册用户
刚刚接触C++, 分析仔细,印象深刻,谢谢了。
2010年08月19日 23:26


盛古蜗牛
不错,对我来说,,
学习一下,,
2010年08月19日 23:45


冬雪
呵呵,lz让我学了不少东西啊,谢谢了
2010年08月20日 01:01


Ray.Mon
非常透彻,受教了!
2010年08月20日 20:12


我要互包
什么时候C++能支持互相包函就好了
反正 互相包函的都要重新重成OBJ

或者加一个选项 是否支持互包函


支持互包函就完美了 不管是怎么实现 只要有这功能 就好了

现在CPU这么好 不差这些编译时间 龙其是小工程的项目

2010年08月20日 23:11


dwd
这文章写了半天说了一个不是问题的问题。
为什么A和B中不能直接彼此定义对方的实例还用说那么多么,如果那么搞的话A和B的内存结构就无法确定了因为形成了一个递归定义,这和语言 无关任何语言都不会允许你那么搞因为从逻辑上说根本就是不可能的。连不懂编程的人想一想都能想明白,还用得着写这么长一串还引入了一大堆别 的概念么。。反倒把简单的问题复杂化了
2010年09月17日 00:58


范德萨
为什么我们前置声明时,只能使用类型的指针和引用呢?
这个问题解答了吗??

 引用 (4)


 写评论  

欲获得技术支持,请访问软件支持页面.
姓名 (必填)*

电子邮件 (必填,不在本页面显示)*

您的 URL (可选)


评论*