在C++中,以下之间是否有任何区别:
struct Foo { ... };
和:
typedef struct { ... } Foo;
C++没有区别,但我相信C会允许您声明struct Foo的实例而无需显式执行:
struct Foo bar;
在C++,只有细微的差别。这是C语言的延续,在这一点上它是有区别的。
C语言标准(C89§3.1.2.3、C11§6.2.3.2.3" rel="noreferrer">C99§6.2.3和C11§6.2.3)为不同类别的标识符规定了单独的命名空间,包括标签标识符(用于struct/union/enum)和普通标识符(用于typedef和其他标识符)。
struct
union
enum
typedef
如果你只是说:
struct Foo { ... };Foo x;
您将收到编译器错误,因为Foo仅在标记命名空间中定义。
Foo
您必须将其声明为:
struct Foo x;
任何时候你想引用一个Foo,你总是必须称之为struct Foo。这很快就会让人讨厌,所以你可以添加一个typedef:
struct Foo
struct Foo { ... };typedef struct Foo Foo;
现在struct Foo(在标记命名空间中)和普通的Foo(在普通标识符命名空间中)都指同样的事情,您可以在没有struct关键字的情况下自由声明Foo类型的对象。
构造:
typedef struct Foo { ... } Foo;
只是声明和typedef的缩写。
最后,
声明一个匿名结构并为其创建一个typedef。因此,使用此构造,它在标记命名空间中没有名称,只有在typedef命名空间中的名称。这意味着它也不能向前声明。如果你想做一个前置声明,你必须在标签命名空间中给它一个名字。
在C++,所有struct/union/enum/class声明都表现为隐式typedef'ed,只要名称不被同名的另一个声明隐藏。
class
是有区别,但很微妙。这样看:struct Foo引入了一个新类型。第二个为未命名的struct类型创建了一个名为Foo的别名(而不是新类型)。
7.1.3 typedef说明符 1[…] 使用typedef说明符声明的名称成为typedef名称。在其声明的范围内,一个typedef-name在语法上等价于关键字,并命名与中的标识符关联的类型第8条中描述的方式。因此,typedef-name是另一种类型的同义词。typedef-name不引入新类型就像类声明(9.1)或枚举声明一样。 8如果typedef声明定义了一个未命名的类(或枚举),则声明声明的第一个typedef-nameto be类类型(或枚举类型)用于表示链接的类类型(或枚举类型)仅用于(3.5)。[示例:
7.1.3 typedef说明符
1[…]
使用typedef说明符声明的名称成为typedef名称。在其声明的范围内,一个typedef-name在语法上等价于关键字,并命名与中的标识符关联的类型第8条中描述的方式。因此,typedef-name是另一种类型的同义词。typedef-name不引入新类型就像类声明(9.1)或枚举声明一样。
8如果typedef声明定义了一个未命名的类(或枚举),则声明声明的第一个typedef-nameto be类类型(或枚举类型)用于表示链接的类类型(或枚举类型)仅用于(3.5)。[示例:
typedef struct { } *ps, S; // S is the class name for linkage purposes
因此,typedef总是用作另一种类型的占位符/同义词。
还有一个更重要的区别:typedef不能向前声明。因此,对于typedef选项,您必须#include包含typedef的文件,这意味着#include的所有内容您的.h也包括该文件,无论它是否直接需要它,等等。它肯定会影响您在大型项目上的构建时间。
#include
.h
如果没有typedef,在某些情况下,您可以在.h文件的顶部添加struct Foo;的前置声明,在.cpp文件中仅添加#include的结构定义。
struct Foo;
.cpp
在这篇DDJ文章中,Dan Saks解释了一个小区域,如果您不输入结构体(和类!),错误可能会悄悄通过:
如果你愿意,你可以想象C++为每个标签生成一个typedef名称,例如 typedef class string string; 不幸的是,这并不完全准确。我也希望有那么简单,但事实并非如此C++无法产生结构体、联合或枚举的typedef不引入不兼容性C. 例如,假设一个C程序同时声明一个函数和一个结构命名状态: int status(); struct status; 同样,这可能是不好的做法,但是在这个程序中,状态(由本身)指的是函数;结构状态是指类型。 如果C++自动生成标签的typedef,然后当你将此程序编译为C++编译器将生成: typedef struct status status; 不幸的是,这种类型的名称与函数名称冲突,并且程序无法编译。那是为什么C++不能简单地生成一个每个标签的typedef。 在C++,标签就像typedef一样名称,除了一个程序可以声明一个对象、函数或具有相同名称的枚举器和与标签相同的范围。在这种情况下,对象、函数或枚举器名称隐藏标签名称。程序可以仅通过使用引用标记名称关键字class、struct、Union或enum(视情况而定)在标记名称。由以下内容组成的类型名称其中一个关键字后面跟着一个tag是一个精心设计的类型说明符。例如,struct状态和枚举月是详细的类型说明符。 因此,一个C程序包含: int status(); struct status; 编译为C++时的行为相同。名称状态单独指的是函数。该程序可以引用类型只使用详细类型说明符结构状态。 那么这是如何让错误蔓延的呢?进入程序?考虑程序在清单1。这个程序定义了一个使用默认构造函数的类foo,和一个转换运算符将foo对象转换为char const*。表达式 p = foo(); 在main中应该构造一个foo对象并应用转换运算符。该后续输出语句 cout << p << '\n'; 应该显示foo类,但它不显示。它显示函数foo。 这个令人惊讶的结果发生是因为该程序包括头lib. h显示在清单2。这个标题定义了一个名为foo的函数。The函数名foo隐藏类名foo,所以在main中对foo的引用指的是函数,而不是类。main只能通过以下方式引用类使用精心设计的类型说明符,如在 p = class foo(); 避免这种混乱的方法在整个程序中添加以下typedef为类名foo: typedef class foo foo; 在上课之前或之后定义。此typedef导致类型名称foo和函数名称foo(来自库)这将触发一个编译时错误。 我知道没有人真正写过这些typedef是理所当然的。这需要很多纪律。因为错误的发生率,例如清单1中的一个可能很漂亮小,你从来没有碰过这个问题。但如果您的错误软件可能会导致身体伤害,那么你应该写typedef no不管错误有多不可能。 我无法想象为什么会有人想要隐藏一个类名函数或对象名称在同一作用域作为类。隐藏规则C是一个错误,他们应该未扩展到中的班级C++。的确,你可以纠正错误,但需要额外的编程纪律和努力这是不必要的
如果你愿意,你可以想象C++为每个标签生成一个typedef名称,例如
typedef class string string;
不幸的是,这并不完全准确。我也希望有那么简单,但事实并非如此C++无法产生结构体、联合或枚举的typedef不引入不兼容性C.
例如,假设一个C程序同时声明一个函数和一个结构命名状态:
int status(); struct status;
同样,这可能是不好的做法,但是在这个程序中,状态(由本身)指的是函数;结构状态是指类型。
如果C++自动生成标签的typedef,然后当你将此程序编译为C++编译器将生成:
typedef struct status status;
不幸的是,这种类型的名称与函数名称冲突,并且程序无法编译。那是为什么C++不能简单地生成一个每个标签的typedef。
在C++,标签就像typedef一样名称,除了一个程序可以声明一个对象、函数或具有相同名称的枚举器和与标签相同的范围。在这种情况下,对象、函数或枚举器名称隐藏标签名称。程序可以仅通过使用引用标记名称关键字class、struct、Union或enum(视情况而定)在标记名称。由以下内容组成的类型名称其中一个关键字后面跟着一个tag是一个精心设计的类型说明符。例如,struct状态和枚举月是详细的类型说明符。
因此,一个C程序包含:
编译为C++时的行为相同。名称状态单独指的是函数。该程序可以引用类型只使用详细类型说明符结构状态。
那么这是如何让错误蔓延的呢?进入程序?考虑程序在清单1。这个程序定义了一个使用默认构造函数的类foo,和一个转换运算符将foo对象转换为char const*。表达式
p = foo();
在main中应该构造一个foo对象并应用转换运算符。该后续输出语句
cout << p << '\n';
应该显示foo类,但它不显示。它显示函数foo。
这个令人惊讶的结果发生是因为该程序包括头lib. h显示在清单2。这个标题定义了一个名为foo的函数。The函数名foo隐藏类名foo,所以在main中对foo的引用指的是函数,而不是类。main只能通过以下方式引用类使用精心设计的类型说明符,如在
p = class foo();
避免这种混乱的方法在整个程序中添加以下typedef为类名foo:
typedef class foo foo;
在上课之前或之后定义。此typedef导致类型名称foo和函数名称foo(来自库)这将触发一个编译时错误。
我知道没有人真正写过这些typedef是理所当然的。这需要很多纪律。因为错误的发生率,例如清单1中的一个可能很漂亮小,你从来没有碰过这个问题。但如果您的错误软件可能会导致身体伤害,那么你应该写typedef no不管错误有多不可能。
我无法想象为什么会有人想要隐藏一个类名函数或对象名称在同一作用域作为类。隐藏规则C是一个错误,他们应该未扩展到中的班级C++。的确,你可以纠正错误,但需要额外的编程纪律和努力这是不必要的
不能将前置声明与typedef结构一起使用。
结构本身是一个匿名类型,所以你没有一个实际的名称来转发声明。
typedef struct{int one;int two;}myStruct;
像这样的前置声明是行不通的:
struct myStruct; //forward declaration fails void blah(myStruct* pStruct); //error C2371: 'myStruct' : redefinition; different basic types
C++中'typedef struct'和'struct'的一个重要区别是'typedef structs'中的内联成员初始化将不起作用。
// the 'x' in this struct will NOT be initialised to zerotypedef struct { int x = 0; } Foo; // the 'x' in this struct WILL be initialised to zerostruct Foo { int x = 0; };
结构是创建数据类型。typedef是为数据类型设置昵称。