当前位置:  开发笔记 > 编程语言 > 正文

C++中'struct'和'typedef struct'之间的区别?

如何解决《C++中'struct'和'typedefstruct'之间的区别?》经验,为你挑选了5个好方法。

在C++中,之间有什么区别:

struct Foo { ... };

typedef struct { ... } Foo;

Adam Rosenfi.. 1147

在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;

任何时候你想要参考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/ union/ enum/ class声明就像它们被隐式地typedef一样.有关详细信息,请参阅Michael Burr的答案.



1> Adam Rosenfi..:

在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;

任何时候你想要参考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/ union/ enum/ class声明就像它们被隐式地typedef一样.有关详细信息,请参阅Michael Burr的答案.


虽然你说的是真的,AFAIK,声明,'typedef struct {...} Foo;' 为未命名的struct创建别名.
好的捕获,"typedef struct Foo {...} Foo"之间有一个微妙的区别; 和"typedef struct {...} Foo;".
除了not-type-typedef之外,问题中两段代码之间的另一个区别是Foo可以在第一个示例中定义构造函数,但不在第二个示例中定义(因为匿名类不能定义构造函数或析构函数) .
在C中,struct标签,union标签和枚举标签共享一个名称空间,而不是(struct and union)使用两个如上所述; 为typedef名称引用的名称空间确实是独立的.这意味着你不能同时拥有'union x {...};' 和'struct x {...};' 在一个范围内.

2> 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.

这种令人惊奇的结果的发生是因为该程序包括在标题所示lib.h 清单2.此标头定义了一个名为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++中的类.实际上,您可以纠正错误,但它需要额外的编程规则和努力,这是不必要的.


如果你尝试"class foo()"并且它失败了:在ISO C++中,"class foo()"是一个非法的构造(文章写成'97,在标准化之前,似乎).你可以把"typedef class foo foo;" 进入主,然后你可以说"foo();" (因为那时,typedef-name在词法上比函数名更接近).从语法上讲,在T()中,T必须是一个简单类型说明符.不允许使用详细的类型说明符.当然,这仍然是一个很好的答案.
如果对类和函数使用不同的命名约定,则还可以避免命名冲突,而无需添加额外的typedef.

3> Joe..:

一个更重要的区别:typedefs不能向前声明.因此,对于该typedef选项,您必须#include包含该文件typedef,这意味着#include您的所有内容.h也包括该文件是否直接需要它,等等.它肯定会影响您在大型项目上的构建时间.

如果没有typedef,在某些情况下,您只需struct Foo;.h文件顶部添加前向声明,并在文件中只添加#include结构定义.cpp.


它不是真正的额外检查,它只是编译器需要处理的更多代码.对于在其包含链中的某个地方遇到typedef的每个cpp文件,它都会编译typedef.在较大的项目中,包含typedef的.h文件很容易被编译数百次,尽管预编译的头文件有很多帮助.如果你可以使用前向声明,那么它更容易将包含完整结构规范的.h仅包含在真正关心的代码中,因此相应的包含文件的编译频率较低.

4> dirkgently..:

这里一个区别,但微妙.这样看:struct Foo引入一种新类型.第二个为未命名的struct类型创建一个名为Foo(而不是新类型)的别名.

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

因此,typedef 始终用作另一种类型的占位符/同义词.



5> Yochai Timme..:

您不能对typedef结构使用forward声明.

struct本身是一个匿名类型,因此您没有实际名称来转发声明.

typedef struct{
    int one;
    int two;
}myStruct;

像这样的前瞻声明不会起作用:

struct myStruct; //forward declaration fails

void blah(myStruct* pStruct);

//error C2371: 'myStruct' : redefinition; different basic types

推荐阅读
郑小蒜9299_941611_G
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有