刚开始学习C
++的人都会遇到这样的问题:
定义一个类
class A,这个类里面使用了类
B的对象
b,然后定义了一个类
B,里面也包含了一个类
A的对象
a,就成了这样:
//a.h#include "b.h"class A{....private:B b;};//另一个头文件b.h#include "a.h"class B{....private:A a;};一编译,就出现了一个互包含的问题了,这时就有人跳出来说,这个问题的解决办法可以这样,在
a.h文件中声明类
B,然后使用B的指针。
//a.h //#include "b.h"class B; class A { .... private: B *b; }; //b.h #include "a.h" class B{ .... private: A a; };然后,问题就解决了。但是,有人知道问题是为什么就被解决的吗,也就是说,加了个
前置声明为什么就解决了这样的问题。下面,让我来探讨一下这个前置声明。类的前置声明是有许多的好处的。我们使用前置声明的一个好处是,从上面看到,当我们在类
A使用类
B的前置声明时,我们修改类
B时,只需要重新编译类
B,而不需要重新编译
a.h的(当然,在真正使用类
B时,必须包含
b.h)。另外一个好处是减小类
A的大小,上面的代码没有体现,那么我们来看下:
//a.hclass B;class A{....private:B *b;....};//b.hclass B{....private:int a;int b;int c;};我们看上面的代码,类
B的大小是12(在32位机子上)。
如果我们在类
A中包含的是
B的对象,那么类
A的大小就是12(假设没有其它成员变量和虚函数)。如果包含的是类
B的指针
*b变量,那么类
A的大小就是4,所以这样是可以减少类
A的大小的,特别是对于在
STL的容器里包含的是类的对象而不是指针的时候,这个就特别有用了。在前置声明时,我们只能使用的就是类的指针和引用(因为引用也是居于指针的实现的)。那么,问你一个问题,
为什么前置声明只能使用类型的指针和引用呢?如果你回答到:那是因为
指针是固定大小,并且可以表示任意的类型,那么可以给你80分了。为什么只有80分,因为还没有完全回答到。想要更详细的答案,我们看下下面这个类:
class A{public:A(int a):_a(a),_b(_a){} // _b is new addint get_a() const {return _a;}int get_b() const {return _b;} // new addprivate:int _b; // new addint _a;};我们看下上面定义的这个类
A,其中
_b变量和
get_b()函数是新增加进这个类的。
那么我问你,在增加进
_b变量和
get_b()成员函数后这个类发生了什么改变,思考一下再回答。好了,我们来列举这些改变:
- 增加了
_b变量和get_b()成员函数; - 这个类的大小改变了,原来是4,现在是8。
- 成员
_a的偏移地址改变了,原来相对于类的偏移是0,现在是4了。
上面的改变都是我们显式的、看得到的改变。还有一个隐藏的改变,想想是什么。。。
这个隐藏的改变是
类A的默认构造函数和默认拷贝构造函数发生了改变。
由上面的改变可以看到,任何调用类
A的成员变量或成员函数的行为都需要改变,因此,我们的
a.h需要重新编译。如果我们的
b.h是这样的:
//b.h#include "a.h"class B{...private:A a;}; 那么我们的
b.h也需要重新编译。如果是这样的:
//b.hclass A;class B{...private:A *a;}; 那么我们的
b.h就不需要重新编译。像我们这样前置声明类
A:
class A;是一种不完整的声明,只要类
B中没有执行需要了解类
A的大小或者成员的操作,则这样的不完整声明允许声明指向
A的指针和引用。
而在前一个代码中的语句
A a;是需要了解
A的大小的,不然是不可能知道如果给类
B分配内存大小的,因此不完整的前置声明就不行,必须要包含
a.h来获得类
A的大小,同时也要重新编译类
B。再回到前面的问题,使用前置声明只允许的声明是指针或引用的一个原因是只要
*这个声明没有执行需要了解类A的大小或者成员的操作就可以了,所以声明成指针或引用是没有执行需要了解类
A的大小或者成员的操作的。------------------------------分割线------------------------------
C++ Primer Plus 第6版 中文版 清晰有书签PDF+源代码 http://www.linuxidc.com/Linux/2014-05/101227.htm读C++ Primer 之构造函数陷阱 http://www.linuxidc.com/Linux/2011-08/40176.htm读C++ Primer 之智能指针 http://www.linuxidc.com/Linux/2011-08/40177.htm读C++ Primer 之句柄类 http://www.linuxidc.com/Linux/2011-08/40175.htm
将C语言梳理一下,分布在以下10个章节中:- Linux-C成长之路(一):Linux下C编程概要 http://www.linuxidc.com/Linux/2014-05/101242.htm
- Linux-C成长之路(二):基本数据类型 http://www.linuxidc.com/Linux/2014-05/101242p2.htm
- Linux-C成长之路(三):基本IO函数操作 http://www.linuxidc.com/Linux/2014-05/101242p3.htm
- Linux-C成长之路(四):运算符 http://www.linuxidc.com/Linux/2014-05/101242p4.htm
- Linux-C成长之路(五):控制流 http://www.linuxidc.com/Linux/2014-05/101242p5.htm
- Linux-C成长之路(六):函数要义 http://www.linuxidc.com/Linux/2014-05/101242p6.htm
- Linux-C成长之路(七):数组与指针 http://www.linuxidc.com/Linux/2014-05/101242p7.htm
- Linux-C成长之路(八):存储类,动态内存 http://www.linuxidc.com/Linux/2014-05/101242p8.htm
- Linux-C成长之路(九):复合数据类型 http://www.linuxidc.com/Linux/2014-05/101242p9.htm
- Linux-C成长之路(十):其他高级议题
本文永久更新链接地址:http://www.linuxidc.com/Linux/2015-04/116028.htm<!-- Baidu Button BEGIN -->