C++ 中“struct”和“typedef struct”的区别?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/612328/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Difference between 'struct' and 'typedef struct' in C++?
提问by criddell
In C++, is there any difference between:
在C++ 中,以下之间有什么区别:
struct Foo { ... };
and:
和:
typedef struct { ... } Foo;
回答by Adam Rosenfield
In C++, there is only a subtle difference. It's a holdover from C, in which it makes a difference.
在 C++ 中,只有细微的差别。它是 C 语言的延续,在这方面有所作为。
The C language standard (C89 §3.1.2.3, C99 §6.2.3, and C11 §6.2.3) mandates separate namespaces for different categories of identifiers, including tag identifiers(for struct
/union
/enum
) and ordinary identifiers(for typedef
and other identifiers).
C语言标准(C89§3.1.2.3,C99第6.2.3节,和C11第6.2.3节)的任务为不同类别的标识符,包括单独的名称空间标签标识符(对于struct
/ union
/ enum
)和普通标识符(用于typedef
和其它标识符) .
If you just said:
如果你只是说:
struct Foo { ... };
Foo x;
you would get a compiler error, because Foo
is only defined in the tag namespace.
你会得到一个编译器错误,因为Foo
它只在标签命名空间中定义。
You'd have to declare it as:
您必须将其声明为:
struct Foo x;
Any time you want to refer to a Foo
, you'd always have to call it a struct Foo
. This gets annoying fast, so you can add a typedef
:
任何时候您想引用 a Foo
,您总是必须将其称为 a struct Foo
。这很快就会变得烦人,因此您可以添加typedef
:
struct Foo { ... };
typedef struct Foo Foo;
Now struct Foo
(in the tag namespace) and just plain Foo
(in the ordinary identifier namespace) both refer to the same thing, and you can freely declare objects of type Foo
without the struct
keyword.
现在struct Foo
(在标签命名空间中)和 just plain Foo
(在普通标识符命名空间中)都指的是同一个东西,你可以自由地声明Foo
没有struct
关键字的类型的对象。
The construct:
构造:
typedef struct Foo { ... } Foo;
is just an abbreviation for the declaration and typedef
.
只是声明和的缩写typedef
。
Finally,
最后,
typedef struct { ... } Foo;
declares an anonymous structure and creates a typedef
for it. Thus, with this construct, it doesn't have a name in the tag namespace, only a name in the typedef namespace. This means it also cannot be forward-declared. If you want to make a forward declaration, you have to give it a name in the tag namespace.
声明一个匿名结构并typedef
为其创建一个。因此,使用此构造,它在标记命名空间中没有名称,只有在 typedef 命名空间中的名称。这意味着它也不能预先声明。 如果要进行前向声明,则必须在标记命名空间中为其指定一个名称。
In C++, all struct
/union
/enum
/class
declarations act like they are implicitly typedef
'ed, as long as the name is not hidden by another declaration with the same name. See Michael Burr's answerfor the full details.
在C ++中,所有struct
/ union
/ enum
/class
声明像他们是隐式typedef
“版,只要名称不与另一个同名的声明所隐藏。有关完整详细信息,请参阅Michael Burr 的回答。
回答by Michael Burr
In this DDJ article, Dan Saks explains one small area where bugs can creep through if you do not typedef your structs (and classes!):
在这篇 DDJ 文章中,Dan Saks 解释了一个小区域,如果您不对结构(和类!)进行类型定义,则错误可能会蔓延:
If you want, you can imagine that C++ generates a typedef for every tag name, such as
typedef class string string;
Unfortunately, this is not entirely accurate. I wish it were that simple, but it's not. C++ can't generate such typedefs for structs, unions, or enums without introducing incompatibilities with C.
For example, suppose a C program declares both a function and a struct named status:
int status(); struct status;
Again, this may be bad practice, but it is C. In this program, status (by itself) refers to the function; struct status refers to the type.
If C++ did automatically generate typedefs for tags, then when you compiled this program as C++, the compiler would generate:
typedef struct status status;
Unfortunately, this type name would conflict with the function name, and the program would not compile. That's why C++ can't simply generate a typedef for each tag.
In C++, tags act just like typedef names, except that a program can declare an object, function, or enumerator with the same name and the same scope as a tag. In that case, the object, function, or enumerator name hides the tag name. The program can refer to the tag name only by using the keyword class, struct, union, or enum (as appropriate) in front of the tag name. A type name consisting of one of these keywords followed by a tag is an elaborated-type-specifier. For instance, struct status and enum month are elaborated-type-specifiers.
Thus, a C program that contains both:
int status(); struct status;
behaves the same when compiled as C++. The name status alone refers to the function. The program can refer to the type only by using the elaborated-type-specifier struct status.
So how does this allow bugs to creep into programs? Consider the program in Listing 1. This program defines a class foo with a default constructor, and a conversion operator that converts a foo object to char const *. The expression
p = foo();
in main should construct a foo object and apply the conversion operator. The subsequent output statement
cout << p << '\n';
should display class foo, but it doesn't. It displays function foo.
This surprising result occurs because the program includes header lib.h shown in Listing 2. This header defines a function also named foo. The function name foo hides the class name foo, so the reference to foo in main refers to the function, not the class. main can refer to the class only by using an elaborated-type-specifier, as in
p = class foo();
The way to avoid such confusion throughout the program is to add the following typedef for the class name foo:
typedef class foo foo;
immediately before or after the class definition. This typedef causes a conflict between the type name foo and the function name foo (from the library) that will trigger a compile-time error.
I know of no one who actually writes these typedefs as a matter of course. It requires a lot of discipline. Since the incidence of errors such as the one in Listing 1is probably pretty small, you many never run afoul of this problem. But if an error in your software might cause bodily injury, then you should write the typedefs no matter how unlikely the error.
I can't imagine why anyone would ever want to hide a class name with a function or object name in the same scope as the class. The hiding rules in C were a mistake, and they should not have been extended to classes in C++. Indeed, you can correct the mistake, but it requires extra programming discipline and effort that should not be necessary.
如果你愿意,你可以想象C++为每个标签名生成一个typedef,比如
typedef class string string;
不幸的是,这并不完全准确。我希望事情就这么简单,但事实并非如此。如果不引入与 C 的不兼容,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(视情况而定)来引用标签名称。由这些关键字之一后跟标签组成的类型名称是详细类型说明符。例如,结构状态和枚举月份是详细的类型说明符。
因此,一个包含以下两者的 C 程序:
int status(); struct status;
编译为 C++ 时的行为相同。名称 status 仅指函数。程序只能通过使用详细类型说明符结构状态来引用类型。
那么这如何允许错误潜入程序中呢?考虑清单 1 中的程序 。该程序定义了一个带有默认构造函数的 foo 类,以及一个将 foo 对象转换为 char const * 的转换运算符。表达方式
p = foo();
在 main 中应该构造一个 foo 对象并应用转换运算符。后续输出语句
cout << p << '\n';
应该显示类 foo,但它没有。它显示函数 foo。
这种令人惊奇的结果的发生是因为该程序包括标题lib.h所示清单2。这个头文件定义了一个名为 foo 的函数。函数名 foo 隐藏了类名 foo,所以 main 中对 foo 的引用指的是函数,而不是类。main 只能通过使用详细类型说明符来引用类,如
p = class foo();
在整个程序中避免这种混淆的方法是为类名 foo 添加以下 typedef:
typedef class foo foo;
紧接在类定义之前或之后。这个 typedef 会导致类型名称 foo 和函数名称 foo(来自库)之间的冲突,这将触发编译时错误。
我知道没有人理所当然地真正编写这些 typedef。它需要很多纪律。由于诸如清单 1 中的错误的发生率可能非常小,因此你们很多人永远不会遇到这个问题。但是,如果您的软件中的错误可能会导致人身伤害,那么无论错误的可能性有多大,您都应该编写 typedef。
我无法想象为什么有人会想要隐藏具有与类相同范围内的函数或对象名称的类名。C 中的隐藏规则是错误的,它们不应该扩展到 C++ 中的类。确实,您可以纠正错误,但这需要额外的编程纪律和工作,而这本不应该是必要的。
回答by Joe
One more important difference: typedef
s cannot be forward declared. So for the typedef
option you must #include
the file containing the typedef
, meaning everything that #include
s your .h
also includes that file whether it directly needs it or not, and so on. It can definitely impact your build times on larger projects.
一个更重要的区别:typedef
s 不能被向前声明。因此,对于该typedef
选项,您必须#include
包含包含 的文件typedef
,这意味着#include
您的所有内容.h
也包括该文件,无论它是否直接需要它,等等。它肯定会影响您在大型项目上的构建时间。
Without the typedef
, in some cases you can just add a forward declaration of struct Foo;
at the top of your .h
file, and only #include
the struct definition in your .cpp
file.
如果没有typedef
,在某些情况下,您只需struct Foo;
在.h
文件顶部添加前向声明,并且仅#include
在.cpp
文件中添加结构定义。
回答by dirkgently
There isa difference, but subtle. Look at it this way: struct Foo
introduces a new type. The second one creates an alias called Foo (and not a new type) for an unnamed struct
type.
这里是一个区别,但微妙。这样看:struct Foo
引入了一个新类型。第二个为未命名struct
类型创建一个名为 Foo(而不是新类型)的别名。
7.1.3 The typedef specifier
1 [...]
A name declared with the typedef specifier becomes a typedef-name. Within the scope of its declaration, a typedef-name is syntactically equivalent to a keyword and names the type associated with the identifier in the way described in Clause 8. A typedef-name is thus a synonym for another type. A typedef-name does not introduce a new typethe way a class declaration (9.1) or enum declaration does.
8 If the typedef declaration defines an unnamed class (or enum), the first typedef-name declared by the declaration to be that class type (or enum type) is used to denote the class type (or enum type) for linkage purposes only (3.5). [ Example:
7.1.3 typedef 说明符
1 [...]
使用 typedef 说明符声明的名称成为 typedef-name。在其声明的范围内,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
So, a typedef alwaysis used as an placeholder/synonym for another type.
因此,typedef始终用作另一种类型的占位符/同义词。
回答by Yochai Timmer
You can't use forward declaration with the typedef struct.
不能对 typedef 结构使用前向声明。
The struct itself is an anonymous type, so you don't have an actual name to forward declare.
结构本身是一种匿名类型,因此您没有要转发声明的实际名称。
typedef struct{
int one;
int two;
}myStruct;
A forward declaration like this wont work:
像这样的前向声明不起作用:
struct myStruct; //forward declaration fails
void blah(myStruct* pStruct);
//error C2371: 'myStruct' : redefinition; different basic types
回答by user2796283
An important difference between a 'typedef struct' and a 'struct' in C++ is that inline member initialisation in 'typedef structs' will not work.
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; };
回答by David Tu
Struct is to create a data type. The typedef is to set a nickname for a data type.
struct 是创建一个数据类型。typedef 是为数据类型设置昵称。
回答by xian
There is no difference in C++, but I believe in C it would allow you to declare instances of the struct Foo without explicitly doing:
C++ 中没有区别,但我相信在 C 中,它允许您在不显式执行的情况下声明 struct Foo 的实例:
struct Foo bar;