如前所述,C/C++ 中的前向声明是实际定义不可用的东西的声明。它是一个声明,告诉编译器“有一个数据类型 ABC”。
让我们假设这是一些键/值存储的标题my_dict.h
:
...
struct my_dict_t;
struct my_dict_t* create();
char* get_value(const struct my_dict_t* dict, const char* name);
char* insert(struct my_dict_t* dict, const char* name, char* value);
void destroy(struct my_dict_t* dict);
...
您对此一无所知my_dict_t
,但实际上,使用商店您不需要知道:
#include "my_dict.h"
...
struct my_dict_t* dict = create();
if(0 != insert(dict, "AnEntry", strdup("AValue"))) {
...
}
...
这样做的原因是:您只对数据结构使用 POINTERS。
指针只是数字,为了处理它们,你不需要知道它们指向什么。
仅当您尝试实际访问它们时,这才有意义,例如
struct my_dict_t* dict = create();
printf("%s\n", dict->value); /* Impossible if only a forward decl is available */
因此,为了实现这些功能,您需要实际定义my_struct_t
. 您可以my_dict.c
像这样在源文件中执行此操作:
#include "my_dict.h"
struct my_dict_t {
char* value;
const char* name;
struct my_dict_t* next;
}
struct my_dict_t* create() {
return calloc(1, sizeof(struct my_dict_t));
}
这在几种情况下很方便,例如
- 为了解决循环类型依赖关系,像 Sergei L. 解释的那样。
- 对于封装,就像上面的例子一样。
所以剩下的问题是:为什么我们不能在使用上面的函数时完全省略前向声明?最后,编译器知道所有dict
都是指针就足够了。
但是,编译器确实会执行类型检查:它需要验证您没有执行类似的操作
...
int i = 12;
char* value = get_value(&i, "MyName");
...
它不需要知道my_dict_t
长什么样子,但它需要知道这&i
不是指针所get_value()
期望的类型。