目录
操作结构成员代码无法通过前置声明解决,只能放到cpp文件里面去
C、C++的头文件和结构都是可以互相包含的,虽然头文件和数据结构是完全不相干的东西,但是嵌套包含这种事,是有其内在的规律的。
两个“东西”的互相包含是不可能的,互相嵌套至少会产生一个“无限”:无限空间、无限时间或者无限精度。
自包含就是递归,函数递归会产生无限层级,从而需要无限时间和无限尺寸的栈空间,结构的递归在编译器计算结构尺寸这一步就会遇上无限。
所以一定要某种机制破坏无限,对于递归函数我们都知道,必须要有结束条件,到了某一步停止递归。
对结构的互相调用呢?我们似乎本能地觉得没什么啊,互相包含,水乳交融嘛,比如
- --示意代码,不严格
- struct A
- {
- struct B b;
- int a;
- }
- struct B
- {
- struct A a;
- int b;
- }
这不挺好吗?但是这个代码注定无法编译,因为结构A有多大?整数4字节,再加上B,B包含一个整数和一个A,就是4+4+A,A又是4+B,就是4+4+4+B,B又是4+A……子子孙孙无穷匮也。
头文件也是如此,A.h包含B.h,B.h又包含a.h,编译器到这里也傻眼了,无限包含停不下来。
当然了,解决头文件的互相包含很简单,对头文件有结束包含的机制:
- #ifndef XXXX_H
- #define XXXX_H
-
- //头文件内容
-
- #endif
或者更简单的:
#pragma once
这是个编译器指令,大部分编译器都支持。
头文件这么做很简单,数据结构包含怎么解决?数据结构的互相包含没法用头文件那种机制解决,压根不能那样做,因为得到的数据结构不是预期的。
那么为什么还能看到很多数据结构互相包含呢?不互相包含为什么需要前置声明呢?
嗯……你指的是这样的代码吗?
- --代码示意,不严格
-
- struct B;--这是前置声明,仅仅说明B是一种结构的名字,具体内容不知道
-
- struct A
- {
- B * b;--这是指针,任何指针都是同样的长度,跟指向的东西没关系
- int a;
- };
看清楚啊,里面只是用到了指针——指针的大小和指向的数据的类型无关。编译器知道B是个类型名就可以生成A了,只有在后续使用b->的时候编译器才需要知道B的具体结构。
即使我们不犯两个结构互相包含这种错误,而且我们也知道用前置声明来解决指针问题,我们也仍然会遇到头文件问题:
- //A.h
- #include "B.h"
- class A
- {
- public:
- int iA;
- void fA(B * b)
- {
- b->iB;
- }
- };
-
-
- //B.h
- #include "A.h"
- class B
- {
- public:
- int iB;
- void fB(A * a)
- {
- a->iA;
- }
- };
由于两个类里面要用到->操作,编译器必须知道指针指向的结构的全部信息,所以必须包含头文件,这就不可避免地发生了头文件互相包含,当然我们在cpp文件里同时包含这两个头文件或仅仅只包含一个的时候,都会发生无限嵌套。
不过前面已经说了头文件有避免无限嵌套的机制,而且一般每个头文件都应该使用这个机制。好吧,假设已经在上面的源码里添加了这个机制,那么处理A.h时发生什么:
于是得到了如下代码:
- //A.h
- //B.h
- //#include "A.h"--已经包含过,忽略
- class B
- {
- public:
- int iB;
- void fB(A * a)
- {
- a->iA;
- }
- };
- class A
- {
- public:
- int iA;
- void fA(B * b)
- {
- b->iB;
- }
- };
-
-
编译器试图编译这个代码,却发现A未定义,即使在B.h加上A的前置声明也只能解决fB的参数“A* a”的问题,不能解决函数体里面的“a->iA”。
这个问题唯一的解决方法就是把函数体放到cpp文件里面去。
(这里是结束)