[TOC]

##问题的引入

《The C Programming Language》里讲到:

If a specifier with a tag but with out a list appears when the tag is not declared,an incomplete tpye is specified.

它包括:

void、 未指定长度的数组 以及具有非指定内容的结构和联合


##它的应用场景

为了将私有模块的内部实现细节对提供公共接口的模块隐藏,在公共接口模块中使用不完全类型:共有头文件中只定义结构体的标识符,实现的定义是有私有头文件传给公有头文件的。

比如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//private.h
/*
下面这个结构定义本身是供解释器内部使用的数据结构,不应该向外公开,所以是隐藏的,
而在公有头文件中定义了一个它的不完全定义CRB_Interpreter
*/
struct CRB_Interpreter_tag{
MEM_Storage interpreter_storage;
MEM_Storage execute_storage;
Variable *variable;
FunctionDefinition *function_list;
StatementList statementlist;
int current_line_number;
}
//public.h
CRB_Interpreter *CRB_create_interpreter(void);
/*
生成解释器的函数CRB_create_interpreter()定义在公有头文件中,
它返回类型为CRB_interpreter,为了支持这样的原型定义必须首先定义CRB_Interpreter结构体。但是不能把 私有头文件中的内部定义直接拿来用放在公有头文件中。
为了应对这种情况,就使用了**不完全类型**
*/
typedef struct CRB_Interpreter_tag CRB_Interpreter;
//(注意public.h中并没有#include "private.h")这种状态的CRB_Interpreter就是不完全类型


##不完全类型什么时候会变成 “完全的”

  • 当后续的一个包含声明列表的一个同样的标识符出现时
  • 即使包含了声明列表的标识符的列表中,该标识符也是不完全的,只有当}出现时,该标识符才完全

##不完全类型的特性

  1. 由于struct 或者 union 不能包含 incomplete type的成员,所以因为上面的第二条原因,结构和联合不能包含自己的实例(因为在自己的声明列表还没有遇到}之前,自己的标识符还是一个incomplete type)。
  2. 但是,structure or union type可以包含自引用类型的结构,比如指向自身实例的指针,因为指向不完全类型的指针可以被声明。
  3. 对不完全类型也不能使用sizeof,因此,在compile time,我们是无法知道一个不完全类型的大小的。

##精髓来了!!!
为什么c/c++中如果定义了两个头文件,其中的类或者结构互相引用(写程序经常会遇到这种情况),这种情况下,要想通过编译,不能在两个头文件中都#include对方,只能在其中一个里面#include,而在另一个被#include的头文件中 对用到的另一个文件中定义的类或者结构进行声明其实这个声明就是一个不完全类型啊)

关于这个特性,《The C Programming Language》p213中有一段话特别精髓:

A very special rule applies to declarations of the form struct-or-union identifier; that declare a structure of union,but have no declaration list and no declarators. Even if the identifier is a structure or union tag already declared in an outer scope, this declaration makes the identifier the tag of a new,incompletely-typed structure or union in the current scope.

this recondite rule is new with ANSI. It is intended to deal with mutually-recursive structures declared in an inner scope,but whose tags might already be declared in the outer scope.

1
2
3
4
5
6
7
8
9
10
11
12
13
#ifndef __COMPLETETYPEB_H__
#define __COMPLETETYPEB_H__
// 这个类中包含了一个头文件中定义的类的指针CompleteClass * others_pointer
// 用#include 的方式让这个编译单元(translation unit)获取关于CompleteClass的类型信息
#include "completetype.h"
class CompleteClassB{
int member;
CompleteClass *others_pointer;
CompleteClassB *self_pointer;
};
#endif
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#ifndef __COMPLETETYPE_H__
#define __COMPLETETYPE_H__
// 由于在这个类中包含了另一个类CompleteClassB的一个指针,但是CompleteClassB已经#include了这个头文件,如果这个头文件再通过#include来获取CompleteCassB的类型信息的话,就会造成mutualy-recursive include,编译不会通过。怎么解决呢?**incomplete type **隆重登场!!!
// incomplete type declaration(不完全类型声明)
class CompleteClassB;
class CompleteClass{
int member;
//Completeclass self_instance;
CompleteClassB *others_pointer;
CompleteClass *self_pointer;
};
#endif

Comments

⬆︎TOP