在 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
一样,只要该名称不被另一个具有相同名称的声明隐藏。有关详细信息,请参阅Michael Burr 的答案 。
在这篇 DDJ 文章中 ,Dan Saks 解释了一个小区域,如果你没有输入你的结构(和类!),那么 bug 就会蔓延开来:
如果需要,可以想象 C ++ 为每个标记名称生成一个 typedef,例如
typedef class string string;
不幸的是,这并不完全准确。我希望它很简单,但事实并非如此。 C ++ 无法为结构,联合或枚举生成此类 typedef 而不会引入与 C 的不兼容性。
例如,假设一个 C 程序声明一个函数和一个名为 status 的结构:
int status(); struct status;
同样,这可能是不好的做法,但它是 C. 在这个程序中,状态(单独)指的是函数; struct status 指的是类型。
如果 C ++ 确实为标记自动生成了 typedef,那么当你将这个程序编译为 C ++ 时,编译器会生成:
typedef struct status status;
不幸的是,这个类型名称会与函数名称冲突,程序将无法编译。这就是为什么 C ++ 不能简单地为每个标签生成一个 typedef。
在 C ++ 中,标签的行为与 typedef 名称类似,除了程序可以声明与标签具有相同名称和相同范围的对象,函数或枚举器。在这种情况下,对象,函数或枚举器名称会隐藏标记名称。程序只能通过在标记名称前面使用关键字 class,struct,union 或 enum(根据需要)来引用标记名称。由这些关键字之一后跟标记组成的类型名称是详细类型说明符。例如,struct status 和 enum month 是 elaborated-type-specifiers。
因此,一个 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 ++ 中的类。实际上,您可以纠正错误,但它需要额外的编程规则和努力,这是不必要的。
一个更重要的区别: typedef
s 不能向前声明。因此,对于typedef
选项,您必须#include
包含typedef
的文件,这意味着#include
s .h
所有内容也包括该文件是否直接需要它,等等。它肯定会影响您在大型项目上的构建时间。
没有typedef
,在某些情况下你可以添加struct Foo;
的前向声明struct Foo;
在.h
文件的顶部,只有#include
.cpp
文件中的结构定义。