为什么可以为不存在的结构创建 typedef?
Why can you create typedefs to a struct that doesn't exist?
以下代码可以正常编译。
header.h:
typedef struct Placeholder_Type* Placeholder;
impl.cpp:
#include "header.h"
void doSomething(Placeholder t) {
(void) t;
}
int main() {
int *a = new int();
doSomething((Placeholder)a);
}
编译命令:
clang++ impl.cpp
类型 Placeholder_Type 在任何地方都不存在,并且在输出二进制文件中不作为符号存在。
为什么为不存在的类型创建 typedef 是合法的?
为什么我可以使用不存在的类型创建函数?
这是否等同于仅使用 void* 但命名为“Placeholder”?
为什么不呢?据我所知,“存在”不是一个官方术语。类型可以被声明但不能定义,也可以被声明和定义。在第一种情况下,类型被认为是不完整的。对于不完整的类型,您无能为力。例如,您不能创建该类型的对象。但是,您可以使用指向不完整类型的指针。编译器需要知道的只是类型的声明。进一步注意 typedef 确实包含 Placeholder_Type
:
的声明
typedef struct Placeholder_Type* Placeholder;
^---------------------^ declares the class named Placeholder_Type
正如在您的代码中一样,您永远不会创建对象或使用 Placeholder_Type
的成员,它会编译。我不是 100% 确定,但我认为也没有 UB。强制转换 int
看起来不太好,但由于您从未真正取消引用指针,所以没有错。
有关更多信息,请参阅与前向声明非常相关的问题:What are forward declarations in C++? When can I use a forward declaration?
struct Placeholder_Type
声明结构 Placeholder_Type
(但不定义它),无论它出现在哪里。即使它在 typedef
内。因此,您不会为不存在的结构创建 typedef,而是为您刚刚声明的结构创建类型定义(如果编译器还不知道它,因此创建)。
至于为什么,是因为这样可以让结构体的定义远离public接口。例如,这是在 C:
中实现不透明对象的典型方法
// mytype.h
typedef struct MytypeImpl* Mytype;
Mytype create_mytype();
void destroy_mytype(Mytype o);
void do_something_with_mytype(Mytype o, int i);
// mytype.c
struct MytypeImpl {
int something;
int otherthing;
};
Mytype create_mytype() {
Mytype o = malloc(sizeof(*o));
o->something = 0;
o->otherthing = 0;
return o;
}
void destroy_mytype(Mytype o) {
free(o);
}
// etc.
当然,个人风格可能在细节上有所不同。但关键是结构的定义在 mytype.c 之外是不可见的,因此没有人可以访问数据成员。
并非所有声明都同时是定义。
例如,您可以声明一个尚未定义的函数:
void func( void );
然后引用名字func
.
或者您可以声明一个 class,例如:
class A
{
friend class B;
//...
};
声明在给定范围内引入名称。
只有在评估上下文中使用名称时,它才必须被定义并且具有完整的类型。
在此声明中
typedef struct Placeholder_Type* Placeholder;
引入了两个名字:Placeholder_Type
和Placeholder
。占位符是指针类型 struct Placeholder_Type*
的别名。指针类型的对象总是完整的对象。您可以使用以下表达式计算其大小:
sizeof( Placeholder )
这与具有指向类型 void
的指针相同。
void *p;
void 类型始终是不完整类型,因为指针类型本身是完整类型。如果您不尝试取消引用指针或应用指针算法,则不需要完整的引用类型。
以下代码可以正常编译。
header.h:
typedef struct Placeholder_Type* Placeholder;
impl.cpp:
#include "header.h"
void doSomething(Placeholder t) {
(void) t;
}
int main() {
int *a = new int();
doSomething((Placeholder)a);
}
编译命令:
clang++ impl.cpp
类型 Placeholder_Type 在任何地方都不存在,并且在输出二进制文件中不作为符号存在。
为什么为不存在的类型创建 typedef 是合法的?
为什么我可以使用不存在的类型创建函数?
这是否等同于仅使用 void* 但命名为“Placeholder”?
为什么不呢?据我所知,“存在”不是一个官方术语。类型可以被声明但不能定义,也可以被声明和定义。在第一种情况下,类型被认为是不完整的。对于不完整的类型,您无能为力。例如,您不能创建该类型的对象。但是,您可以使用指向不完整类型的指针。编译器需要知道的只是类型的声明。进一步注意 typedef 确实包含 Placeholder_Type
:
typedef struct Placeholder_Type* Placeholder;
^---------------------^ declares the class named Placeholder_Type
正如在您的代码中一样,您永远不会创建对象或使用 Placeholder_Type
的成员,它会编译。我不是 100% 确定,但我认为也没有 UB。强制转换 int
看起来不太好,但由于您从未真正取消引用指针,所以没有错。
有关更多信息,请参阅与前向声明非常相关的问题:What are forward declarations in C++? When can I use a forward declaration?
struct Placeholder_Type
声明结构 Placeholder_Type
(但不定义它),无论它出现在哪里。即使它在 typedef
内。因此,您不会为不存在的结构创建 typedef,而是为您刚刚声明的结构创建类型定义(如果编译器还不知道它,因此创建)。
至于为什么,是因为这样可以让结构体的定义远离public接口。例如,这是在 C:
中实现不透明对象的典型方法// mytype.h
typedef struct MytypeImpl* Mytype;
Mytype create_mytype();
void destroy_mytype(Mytype o);
void do_something_with_mytype(Mytype o, int i);
// mytype.c
struct MytypeImpl {
int something;
int otherthing;
};
Mytype create_mytype() {
Mytype o = malloc(sizeof(*o));
o->something = 0;
o->otherthing = 0;
return o;
}
void destroy_mytype(Mytype o) {
free(o);
}
// etc.
当然,个人风格可能在细节上有所不同。但关键是结构的定义在 mytype.c 之外是不可见的,因此没有人可以访问数据成员。
并非所有声明都同时是定义。
例如,您可以声明一个尚未定义的函数:
void func( void );
然后引用名字func
.
或者您可以声明一个 class,例如:
class A
{
friend class B;
//...
};
声明在给定范围内引入名称。
只有在评估上下文中使用名称时,它才必须被定义并且具有完整的类型。
在此声明中
typedef struct Placeholder_Type* Placeholder;
引入了两个名字:Placeholder_Type
和Placeholder
。占位符是指针类型 struct Placeholder_Type*
的别名。指针类型的对象总是完整的对象。您可以使用以下表达式计算其大小:
sizeof( Placeholder )
这与具有指向类型 void
的指针相同。
void *p;
void 类型始终是不完整类型,因为指针类型本身是完整类型。如果您不尝试取消引用指针或应用指针算法,则不需要完整的引用类型。