C ++中'struct'和'typedef struct'之间的区别?
题
在C ++中,是否有任何区别:
struct Foo { ... };
和
typedef struct { ... } Foo;
解决方案
在C ++中,只有一个微妙的区别。这是C的延续,它有所作为。
C语言标准( C89§ 3.1.2.3 , C99§ 6.2.3 ,和 C11§ 6.2.3 )任务分开不同类别标识符的名称空间,包括标记标识符(对于 struct
/ union
/ enum
)和普通标识符(用于 typedef
和其他标识符)。
如果你刚才说:
struct Foo { ... };
Foo x;
您会收到编译器错误,因为 Foo
仅在标记名称空间中定义。
您必须将其声明为:
struct Foo x;
每当你想引用 Foo
时,你总是要把它称为 struct Foo
。这很烦人,所以你可以添加 typedef
:
struct Foo { ... };
typedef struct Foo Foo;
现在 struct Foo
(在标记命名空间中)和普通的 Foo
(在普通的标识符命名空间中)都引用相同的东西,你可以自由地声明对象没有 struct
关键字的 Foo
类型。
构造:
typedef struct Foo { ... } Foo;
只是声明的缩写, typedef
。
最后,
typedef struct { ... } Foo;
声明一个匿名结构并为其创建 typedef
。因此,使用此构造,它在标记名称空间中没有名称,只有typedef名称空间中的名称。这意味着它也无法向前宣布。 如果要进行前向声明,则必须在标记名称空间中为其命名。
在C ++中,所有 struct
/ union
/ enum
/ class
声明就像隐含 typedef
'ed,只要该名称不被另一个具有相同名称的声明隐藏。有关详细信息,请参阅 Michael Burr的回答
其他提示
在这篇DDJ文章中,Dan Saks解释了一个小漏洞可以穿过的区域如果你不键入你的结构(和类!):
如果你愿意,你可以想象C ++ 为每个标记生成一个typedef 名称,例如
typedef class string string;
不幸的是,这并非完全如此 准确。我希望就这么简单, 但事实并非如此。 C ++无法生成这样的 结构,联合或枚举的typedef 没有引入不兼容性 与C.
例如,假设一个C程序 声明函数和结构 命名状态:
int status(); struct status;
同样,这可能是不好的做法,但是 它是C.在这个程序中,状态(通过 本身)指的是功能;结构 status指的是类型。
如果C ++自动生成 标签的typedef,然后当你 把这个程序编译为C ++ ,. 编译器会生成:
typedef struct status status;
不幸的是,这个类型名称会 与函数名冲突,和 该程序不会编译。那是 为什么C ++不能简单地生成一个 每个标签的typedef。
在C ++中,标签就像typedef一样 名称,但程序可以 声明一个对象,函数或 具有相同名称的枚举器 与标签相同的范围。在那种情况下, 对象,函数或枚举器名称 隐藏标签名称。该计划可以 仅通过使用来引用标签名称 关键字class,struct,union或 enum(视情况而定)在前面 标签名称。由...组成的类型名称 其中一个关键字后跟一个 tag是一个精心设计的类型说明符。 例如,struct status和enum 月份是精心设计的类型说明符。
因此,一个包含两者的C程序:
编译为C ++时,p = foo();
的行为相同。 仅名称状态指的是 功能。该程序可以参考 只使用 elaborated-type-specifier结构 状态。
那么这是如何让bug陷入困境的呢? 进入程序?考虑一下该计划 清单1 。该程序定义了一个 使用默认构造函数的类foo, 和转换操作符 将foo对象转换为char const *。 表达式
main中的cout << p << '\n';
应该构造一个foo对象 并应用转换运算符。该 后续输出声明
p = class foo();
应该显示类foo,但它 没有。它显示了函数foo。
这个令人惊讶的结果是因为 该程序包括头lib.h 显示在清单2 中。这个标题 定义了一个名为foo的函数。该 函数名称foo隐藏了类名 foo,所以在foo中引用foo 是指函数,而不是类。 main只能通过引用类 使用精心设计的类型说明符,如 在
typedef class foo foo;
避免这种混乱的方法 整个程序是添加的 跟随类名称的typedef FOO:
<*>课程之前或之后 定义。这种typedef会导致a 类型名称foo和。之间的冲突 函数名称foo(来自 库)将触发一个 编译时错误。
我知道没有人真正写作 这些typedef当然是理所当然的。 它需要很多纪律。以来 错误的发生率如 清单1 中的一个可能很漂亮 很小,你们很多人从不与之发生冲突 这个问题。但如果你的错误 软件可能会造成人身伤害, 那么你应该写typedef no 不管错误多么不可能。
我无法想象为什么有人会这样 想要隐藏一个类名 功能或目标
一个更重要的区别: typedef
s无法向前声明。因此,对于 typedef
选项,您必须 #include
包含 typedef
的文件,这意味着 #include
的所有内容都是您的 .h
还包括该文件是否直接需要它,等等。它肯定会影响你在大型项目上的构建时间。
如果没有 typedef
,在某些情况下,您只需在 .h
文件的顶部添加 struct Foo;
的转发声明即可,只有 #include
.cpp
文件中的结构定义。
有区别,但很微妙。以这种方式看待它: struct Foo
引入了一种新类型。第二个为未命名的 struct
类型创建一个名为Foo(而不是新类型)的别名。
7.1.3 typedef说明符
1 [...]
使用typedef说明符声明的名称将成为typedef-name。在其声明范围内,a typedef-name在语法上等同于关键字,并命名与标识符关联的类型 第8章中描述的方式。因此,typedef-name是另一种类型的同义词。 typedef-name 不会像类声明(9.1)或枚举声明那样引入新类型 。
8如果typedef声明定义了一个未命名的类(或枚举),则声明声明的第一个typedef-name 该类类型(或枚举类型)用于表示链接的类类型(或枚举类型) 仅用途(3.5)。 [例如:
typedef struct { } *ps, S; // S is the class name for linkage purposes
因此,typedef 始终用作另一种类型的占位符/同义词。
您不能将前向声明与typedef结构一起使用。
struct本身是一个匿名类型,因此你没有实际名称来转发声明。
typedef struct{
int one;
int two;
}myStruct;
像这样的前瞻声明不会起作用:
struct myStruct; //forward declaration fails
void blah(myStruct* pStruct);
//error C2371: 'myStruct' : redefinition; different basic types
Struct是创建数据类型。 typedef用于设置数据类型的昵称。
C ++中'typedef struct'和'struct'之间的一个重要区别是'typedef structs'中的内联成员初始化不起作用。
// 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 ++没有区别,但是我相信它会允许你在没有明确地做的情况下声明struct Foo的实例:
struct Foo bar;