和 ()
?struct Foo { ... };
并且:
typedef struct { ... } Foo;
struct
/union
/enum
)和普通标识符(用于typedef
和其他标识符)。struct Foo { ... };
Foo x;
struct Foo x;
任何时候您想引用Foo
,都必须称其为struct Foo
。这很快会让人感到烦恼,因此您可以添加typedef
:
struct Foo { ... };
typedef struct Foo Foo;
struct Foo
(在标签命名空间中)和普通的Foo
(在普通标识符命名空间中)都指代同一物体,你可以自由地声明类型为Foo
的对象而不需要使用struct
关键字。
该结构:
typedef struct Foo { ... } Foo;
这只是声明和typedef
的缩写。
最后,
typedef struct { ... } Foo;
typedef
。因此,使用这种结构,在标签命名空间中它没有名称,只有在typedef
命名空间中有名称。这意味着它也不能被前向声明。如果您想进行前向声明,必须在标签命名空间中给它一个名称。
struct
/union
/enum
/class
声明都像隐式地使用typedef
一样,只要名称没有被另一个具有相同名称的声明隐藏。请参见Michael Burr的答案以获取完整的详细信息。在这篇DDJ文章中,Dan Saks解释了如果你不对你的结构体(和类!)进行typedef的话,bug可能会从哪些小地方悄悄蔓延:
如果你愿意,你可以想象C++为每个标签名称生成一个typedef,比如
typedef class string string;
不幸的是,这并不完全准确。我希望它能如此简单,但事实并非如此。C++不能为结构体、联合体或枚举生成这样的typedef,否则会引入与C不兼容的问题。
例如,假设一个C程序声明了一个名为status的函数和结构体:
int status(); struct status;
再次说明,这可能是不好的编程实践,但它是C语言的。在该程序中,status(本身)指的是函数;结构体status则指的是类型。
如果C ++自动为标签生成typedefs,则在将此程序编译为C ++时,编译器将会生成:
typedef struct status status;
遗憾的是,这个类型名称会与函数名称冲突,程序将无法编译。这就是为什么C++不能仅仅为每个标签生成一个typedef。
在C++中,标签(tag)的作用与typedef名称相同,只是程序可以声明具有相同名称和范围的对象、函数或枚举器作为一个标签。在这种情况下,对象、函数或枚举器名称将隐藏标签名称。程序只能通过在标签名称前使用关键字class、struct、union或enum(视情况而定)来引用标签名称。由一个关键字加上一个标签组成的类型名称称为 elaborated-type-specifier。例如,struct status 和 enum month 都是 elaborated-type-specifiers。
因此,一个包含以下内容的C程序:
int status(); struct status;
使用C++编译后具有相同的行为。名称“status”仅指函数,程序只能使用限定符“struct status”来引用类型。
那么这如何允许程序中出现错误呢?考虑清单1中的程序。此程序定义了一个具有默认构造函数和转换运算符(将foo对象转换为char const *)的类foo。表达式
p = foo();
在主函数中应构造一个foo对象并使用转换运算符。随后输出语句。
cout << p << '\n';
应该显示类foo,但实际上它却显示了函数foo。
这个令人惊讶的结果是因为程序包含了头文件lib.h,该文件在清单2中展示。该头文件定义了一个也叫做foo的函数。函数名foo遮蔽了类名foo,因此main中对foo的引用指向函数而非类。要使用类名foo,main只能通过 elaborated-type-specifier 进行引用,如下:
p = class foo();
为了避免整个程序中出现这样的混淆, 可以为类名foo添加以下typedef:
typedef class foo foo;
在类的定义之前或之后立即使用typedef会导致类型名foo和库中的函数名foo发生冲突,从而触发编译时错误。
我不知道有谁会经常写这些typedef。这需要很多纪律性。由于清单1中出现此类错误的概率可能非常小,您可能永远不会遇到这个问题。但是,如果您的软件中出现的错误可能会导致身体伤害,那么您应该始终编写typedefs,无论错误的可能性有多小。
我无法想象为什么会有人想要将类名与同一范围内的函数或对象名称混淆。在C中隐藏规则是一个错误,它们不应该被扩展到C++中的类。实际上,您可以纠正这个错误,但这需要额外的编程纪律和努力,这是不必要的。
Listing 1
和Listing 2
的链接已经失效,请查看一下。 - Prasoon Saurav还有一个重要的区别: typedef
不能进行前向声明。因此,对于typedef
选项,您必须#include
包含typedef
的文件,这意味着所有包含您的.h
文件的内容都会包含该文件,无论是否直接需要它等等。这肯定会影响大型项目的构建时间。
如果没有typedef
,在某些情况下,您只需在.h
文件的顶部添加struct Foo;
的前向声明,并且仅在.cpp
文件中#include
结构定义即可。
struct tagName
语法时,可以有两个头文件,每个文件都包含一个接受在另一个文件中定义的结构体指针的函数,同时它们之间只有一个结构体定义。 - supercat两者之间有一点微妙的区别。可以这样看待:struct Foo
引入了一个新类型。而第二个则为未命名的struct
类型创建了一个别名Foo(并不是一个新类型)。
7.1.3 typedef关键字
1[...]
使用typedef关键字声明的名称成为typedef名称。在其声明的作用域内,typedef名称在语法上等同于关键字,并以第8条所述的方式命名与标识符相关联的类型。因此,typedef名称是另一种类型的同义词。typedef名称不像类声明(9.1)或枚举声明那样引入新类型。
8 如果typedef声明定义了一个未命名的类(或枚举),则声明中声明的第一个typedef名称用于仅限链接目的地指示该类类型(或枚举类型)(3.5)。[例如:
typedef struct { } *ps, S; // S is the class name for linkage purposes
因此,typedef 始终被用作另一种类型的占位符/同义词。
你不能在typedef结构体中使用前置声明。
结构体本身是一个匿名类型,所以你没有实际的名称来进行前置声明。
typedef struct{
int one;
int two;
}myStruct;
这样的前置声明是无效的:
struct myStruct; //forward declaration fails
void blah(myStruct* pStruct);
//error C2371: 'myStruct' : redefinition; different basic types
myStruct
位于标签命名空间中,而typedef_ed myStruct
位于普通命名空间中,其他标识符如函数名、局部变量名也位于此处。因此不应该有任何冲突...如果你怀疑其中存在错误,我可以向你展示我的代码。 - Richtypedef
时,使用typedef
名称的前向声明并不是指未命名结构。相反,前向声明声明了一个具有标签myStruct
的不完整结构。此外,在没有看到typedef
定义的情况下,使用typedef
名称的函数原型是不合法的。因此,每当我们需要使用myStruct
表示类型时,我们必须包含整个typedef
。如果我理解有误,请纠正我。谢谢。 - Rich// the 'x' in this struct will NOT be initialised to zero
typedef struct { int x = 0; } Foo;
// the 'x' in this struct WILL be initialised to zero
struct Foo { int x = 0; };
在C++中没有区别,但我相信在C中它会允许您声明结构体Foo的实例而不需要显式地执行:
struct Foo bar;
结构体是用来创建数据类型的。 typedef 是为一个数据类型设置一个别名。