在C++中,有什么区别:
struct Foo { ... };
和:
typedef struct { ... } Foo;
在 C++ 中,只有细微的差别。它是 C 的保留,在其中有所作为。
C 语言标准(C89 §3.1.2.3、C99 §6.2.3和C11 §6.2.3)要求为不同类别的标识符使用单独的命名空间,包括标签标识符(for struct
/ union
/ enum
)和普通标识符(fortypedef
和其他标识符) .
如果你只是说:
struct Foo { ... };
Foo x;
你会得到一个编译器错误,因为Foo
只在标签命名空间中定义。
您必须将其声明为:
struct Foo x;
任何时候你想引用 a Foo
,你总是不得不称它为 a 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
的行为就像它们被隐式'ed,只要名称没有被另一个具有相同名称的声明隐藏。有关详细信息,请参阅Michael Burr 的答案。union
enum
class
typedef
在这篇 DDJ 文章中,Dan Saks 解释了一个小区域,如果您不键入定义您的结构(和类!),错误可能会蔓延:
如果你愿意,你可以想象 C++ 为每个标签名生成一个 typedef,比如
typedef class string string;
不幸的是,这并不完全准确。我希望它是那么简单,但事实并非如此。如果不引入与 C 的不兼容性,C++ 无法为结构、联合或枚举生成此类类型定义。
例如,假设一个 C 程序同时声明了一个函数和一个名为 status 的结构:
int status(); struct status;
同样,这可能是不好的做法,但它是 C。在这个程序中,状态(本身)指的是函数;结构状态是指类型。
如果 C++ 确实为标签自动生成 typedef,那么当您将此程序编译为 C++ 时,编译器将生成:
typedef struct status status;
不幸的是,这个类型名会与函数名冲突,程序无法编译。这就是为什么 C++ 不能简单地为每个标签生成一个 typedef。
在 C++ 中,标签的作用与 typedef 名称类似,只是程序可以声明与标签具有相同名称和相同范围的对象、函数或枚举数。在这种情况下,对象、函数或枚举器名称会隐藏标签名称。程序只能通过在标签名称前使用关键字 class、struct、union 或 enum(视情况而定)来引用标签名称。由这些关键字之一和标签组成的类型名称是详细类型说明符。例如,struct status 和 enum month 是详细的类型说明符。
因此,一个包含两者的 C 程序:
int status(); struct status;
编译为 C++ 时的行为相同。名称状态仅指功能。程序只能通过使用详细类型说明符结构状态来引用类型。
那么这如何让错误潜入程序中呢?考虑 清单 1中的程序。该程序定义了一个带有默认构造函数的类 foo,以及一个将 foo 对象转换为 char const * 的转换运算符。表达方式
p = foo();
in main 应该构造一个 foo 对象并应用转换运算符。随后的输出语句
cout << p << '\n';
应该显示类 foo,但它没有。它显示函数 foo。
之所以会出现这个令人惊讶的结果,是因为该程序包含了清单 2中所示的头文件 lib.h。这个头文件定义了一个也叫 foo 的函数。函数名 foo 隐藏了类名 foo,所以 main 中对 foo 的引用指的是函数,而不是类。main 只能通过使用详细类型说明符来引用该类,如
p = class foo();
在整个程序中避免这种混淆的方法是为类名 foo 添加以下 typedef:
typedef class foo foo;
紧接在类定义之前或之后。此 typedef 导致类型名称 foo 和函数名称 foo (来自库)之间的冲突,这将触发编译时错误。
我知道没有人会理所当然地实际编写这些 typedef。它需要大量的纪律。由于诸如清单 1中的错误的发生率可能非常小,因此很多人都不会遇到这个问题。但是,如果您的软件中的错误可能会导致人身伤害,那么无论错误多么不可能,您都应该编写 typedef。
我无法想象为什么有人会想要在与类相同的范围内隐藏具有函数或对象名称的类名称。C 中的隐藏规则是一个错误,它们不应该扩展到 C++ 中的类。确实,您可以纠正错误,但它需要额外的编程纪律和不必要的努力。
还有一个重要的区别:typedef
s 不能被前向声明。因此,对于该typedef
选项,您必须#include
包含包含 的文件typedef
,这意味着#include
您的所有内容.h
也包括该文件,无论它是否直接需要它,依此类推。它肯定会影响您在大型项目上的构建时间。
如果没有typedef
,在某些情况下,您只需在文件struct Foo;
顶部添加前向声明.h
,并且仅#include
在文件中添加结构定义.cpp
。
有区别,但很微妙。这样看:struct Foo
引入了一种新类型。第二个为未命名的类型创建一个名为 Foo 的别名(而不是新类型)struct
。
7.1.3 typedef 说明符
1 [...]
使用 typedef 说明符声明的名称成为 typedef-name。在其声明的范围内,typedef-name 在语法上等价于关键字,并以第 8 节中描述的方式命名与标识符关联的类型。因此 typedef-name 是另一种类型的同义词。typedef-name不会像类声明 (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
'typedef struct' 和 C++ 中的'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++ 没有区别,但我相信在 C 中,它允许您声明 struct Foo 的实例而无需显式执行:
struct Foo bar;
struct 是创建一个数据类型。typedef 是为一个数据类型设置一个昵称。