在C ++中,两者之间是否有任何区别:
struct Foo { ... };
和:
typedef struct { ... } Foo;
在C ++中,两者之间是否有任何区别:
struct Foo { ... };
和:
typedef struct { ... } Foo;
Answers:
在C ++中,只有细微的差别。这是C的遗留物,在其中有所作为。
C语言标准(C89§3.1.2.3,C99§6.2.3和C11§6.2.3)针对不同类别的标识符(包括标签标识符(用于struct
/ union
/ enum
)和普通标识符(for typedef
和其他标识符))规定了单独的命名空间。。
如果您只是说:
struct Foo { ... };
Foo x;
您将得到一个编译器错误,因为Foo
它仅在标记名称空间中定义。
您必须将其声明为:
struct Foo x;
每当您要引用a时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名称空间中只有名称。这意味着它也不能被预先声明。 如果要进行前向声明,则必须在标签命名空间中为其命名。
在C ++中,所有struct
/ union
/ enum
/ class
声明像他们是隐式typedef
“版,只要名称不与另一个同名的声明所隐藏。有关完整的详细信息,请参见Michael Burr的答案。
在DDJ的这篇文章中,Dan Saks解释了一个小区域,如果您不对结构(和类!)进行typedef定义,则漏洞可能会蔓延到下面:
如果需要,您可以想象C ++为每个标记名称生成一个typedef,例如
typedef class string string;
不幸的是,这并不完全准确。我希望它是如此简单,但事实并非如此。如果不引入与C的不兼容性,C ++就无法为结构,联合或枚举生成此类typedef。
例如,假设C程序同时声明了一个函数和一个名为status的结构:
int status(); struct status;
再次,这可能是不好的做法,但是它是C。在此程序中,状态(本身)是指功能;结构状态是指类型。
如果C ++确实为标签自动生成了typedef,那么当您将该程序编译为C ++时,编译器将生成:
typedef struct status status;
不幸的是,此类型名称将与函数名称冲突,并且程序将无法编译。这就是C ++不能简单地为每个标签生成typedef的原因。
在C ++中,标记的作用与typedef名称相同,不同之处在于程序可以使用与标记相同的名称和作用域声明一个对象,函数或枚举器。在这种情况下,对象,函数或枚举器名称将隐藏标签名称。该程序只能通过在标签名称前面使用关键字class,struct,union或enum(视情况而定)来引用标签名称。由这些关键字之一和标签组成的类型名称是精心设计的类型说明符。例如,结构状态和枚举月是详尽的类型说明符。
因此,一个包含以下两个方面的C程序:
int status(); struct status;
与C ++编译时的行为相同。名称状态仅指功能。该程序只能使用Elaborated-type-specifier结构状态来引用类型。
那么,这如何使错误爬到程序中呢?考虑清单1中的程序 。该程序使用默认构造函数定义foo类,并使用一个转换运算符将foo对象转换为char const *。表达方式
p = foo();
在main中应该构造一个foo对象并应用转换运算符。后续输出语句
cout << p << '\n';
应该显示foo类,但不显示。它显示函数foo。
由于程序包含清单2中所示的头文件lib.h,所以出现了令人惊讶的结果。此标头定义了一个也称为foo的函数。函数名称foo隐藏了类名称foo,因此main中对foo的引用是函数而不是类。main只能使用elaborated-type-specifier来引用该类,如
p = class foo();
在整个程序中避免这种混乱的方法是为类名foo添加以下typedef:
typedef class foo foo;
在类定义之前或之后。此typedef导致类型名称foo和函数名称foo(来自库)之间发生冲突,这将触发编译时错误。
当然,我不知道实际上有谁写过这些typedef。这需要很多纪律。由于清单1中的错误的发生率可能很小,因此许多人从未遇到过此问题。但是,如果软件中的错误可能导致人身伤害,则无论错误发生的可能性如何,都应编写typedef。
我无法想象为什么有人会在与类相同的作用域中隐藏带有函数或对象名称的类名称。C中的隐藏规则是一个错误,不应将其扩展到C ++中的类。确实,您可以纠正该错误,但是它需要不必要的额外编程纪律和精力。
Listing 1
和Listing 2
链接断开。看一看。
另一个重要的区别是:typedef
s不能向前声明。因此,对于该typedef
选项,您必须#include
包含的文件typedef
,这意味着#include
s的所有内容.h
还包括该文件,无论它是否直接需要它,等等。它肯定会影响大型项目的构建时间。
如果没有使用typedef
,在某些情况下,您可以仅struct Foo;
在.h
文件顶部添加前向声明,而在文件中仅添加#include
结构定义.cpp
。
这里是一个区别,但微妙。这样看:struct Foo
引入了一种新类型。第二个为未命名struct
类型创建一个称为Foo的别名(而不是新类型)。
7.1.3 typedef说明符
1 [...]
使用typedef说明符声明的名称将变为typedef名称。在其声明范围内,typedef-name在语法上等同于关键字,并按照第8章中所述的方式命名与标识符关联的类型。typedef-name因此是另一种类型的同义词。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
位于普通名称空间中,其他标识符(如函数名称,局部变量名称)位于其中。因此,应该没有任何冲突。如果您怀疑其中是否有错误,可以向我展示我的代码。
typedef
带有typedef
ed名称的正向声明时,不会引用未命名的结构。相反,前向声明使用tag声明了不完整的结构myStruct
。同样,在没有看到的定义的情况下typedef
,使用typedef
ed名称的函数原型也不合法。因此,每当需要myStruct
用来表示类型时,就必须包括整个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; };
x
被初始化。 请参阅Coliru在线IDE中的测试(我将其初始化为42,所以赋值确实发生了,比起零更明显)。