重要的事情说三遍:
类型别名(typedef / using)
类型别名是可以识别类型的不同名称。在 C++ 中,任何有效类型都可以有一个别名,以便可以用不同的标识符来引用它。
在 C++ 中,有两种语法来创建这种类型别名:第一种是从 C 语言继承而来的,使用 typedef
关键字:
typedef existing_type new_type_name;
其中 existing_type
可以是任何类型,无论是基本类型还是复合类型,new_type_name
是赋予该类型的新名称的标识符。
例如:
typedef char C;
typedef unsigned int WORD;
typedef char * pChar;
typedef char field[50];
这定义了四个类型别名:C
、WORD
、pChar
和 field
分别为 char
、unsigned int
、char*
和 char[50]
。一旦这些别名被定义,它们可以像任何其他有效类型一样在声明中使用:
C mychar, anotherchar, *ptc1;
WORD myword;
pChar ptc2;
field name;
最近,C++ 中引入了第二种定义类型别名的语法:
using new_type_name = existing_type;
例如,上述类型别名也可以定义为:
using C = char;
using WORD = unsigned int;
using pChar = char *;
using field = char[50];
用 typedef
定义的别名和用 using
定义的别名在语义上是等价的。唯一的区别是 typedef
在模板方面有某些限制,而 using
没有。因此,using
更通用,尽管 typedef
历史更悠久,在现有代码中可能更常见。
请注意,无论是 typedef
还是 using
都不会创建新的独立数据类型。它们只会创建现有类型的同义词。这意味着上面用 WORD
声明的 myword
类型也可以被认为是 unsigned int
类型;这并不重要,因为两者实际上都指向同一类型。
类型别名可以用于减少长或复杂类型名称的长度,但它们最有用的是作为工具来使程序与它们使用的底层类型分离。例如,通过使用 int
的别名来引用特定类型的参数,而不是直接使用 int
,可以在以后版本中轻松将类型替换为 long
(或其他类型),而无需更改使用该类型的每个实例。
联合(Unions)
联合允许一个内存段以不同的数据类型进行访问。它的声明和使用与结构相似,但功能完全不同:
union type_name {
member_type1 member_name1;
member_type2 member_name2;
member_type3 member_name3;
.
.
} object_names;
这创建了一个新的联合类型,由 type_name
标识,其中所有成员元素占据内存中的相同物理空间。该类型的大小是最大成员元素的大小。例如:
union mytypes_t {
char c;
int i;
float f;
} mytypes;
声明了一个具有三个成员的对象(mytypes
):
mytypes.c
mytypes.i
mytypes.f
这些成员中的每一个都是不同的数据类型。但由于它们都引用内存中的同一位置,因此修改其中一个成员将影响所有成员的值。不可能以独立于其他成员的方式存储不同的值。
联合的一个用途是能够以其全部或作为较小元素的数组或结构访问一个值。例如:
union mix_t {
int l;
struct {
short hi;
short lo;
} s;
char c[4];
} mix;
如果我们假设程序运行的系统中 int
类型的大小为 4 字节,short
类型的大小为 2 字节,则上面定义的联合允许访问相同的 4 字节组:mix.l
、mix.s
和 mix.c
,我们可以根据希望如何访问这些字节来使用它们:作为 int
类型的单个值,或作为两个 short
类型的值,或作为 char
元素数组。该示例在联合中混合了类型、数组和结构,以演示访问数据的不同方式。对于小端系统,这个联合可以表示为:
联合成员在内存中的确切对齐和顺序取决于系统,可能会造成可移植性问题。
匿名联合
当联合是类(或结构)的成员时,它们可以声明为无名称。在这种情况下,它们成为匿名联合,可以通过其成员名称直接从对象访问其成员。例如,看看这两个结构声明之间的区别:
// 具有常规联合的结构
struct book1_t {
char title[50];
char author[50];
union {
float dollars;
int yen;
} price;
} book1;
// 具有匿名联合的结构
struct book2_t {
char title[50];
char author[50];
union {
float dollars;
int yen;
};
} book2;
这两种类型之间的唯一区别是,在第一种类型中,成员联合具有名称(price
),而在第二种类型中则没有。这影响了访问对象成员 dollars
和 yen
的方式。对于第一种类型的对象(具有常规联合),它是:
book1.price.dollars
book1.price.yen
而对于第二种类型的对象(具有匿名联合),它是:
book2.dollars
book2.yen
再次提醒,由于它是成员联合(而不是成员结构),成员 dollars
和 yen
实际上共享相同的内存位置,因此它们不能同时用于存储两个不同的值。价格可以用 dollars
或 yen
表示,但不能同时表示两者。
枚举类型(enum)
枚举类型是用一组自定义标识符(称为枚举值)定义的类型作为可能的值。此枚举类型的对象可以取这些枚举值中的任何一个作为值。
其语法为:
enum type_name {
value1,
value2,
value3,
.
.
} object_names;
这创建了类型 type_name
,其可以取 value1
、value2
、value3
等作为值。可以直接实例化该类型的对象(变量)作为 object_names
。
例如,可以通过以下声明定义一个名为 colors_t
的新变量类型来存储颜色:
enum colors_t {black, blue, green, cyan, red, purple, yellow, white};
注意,这个声明中没有包含任何其他类型,无论是基本类型还是复合类型。换句话说,这在某种程度上创建了一种全新的数据类型,而不是基于任何其他现有类型。此新类型 color_t
变量可能的值是列在大括号内的枚举值。例如,一旦声明了 colors_t
枚举类型,以下表达式将是有效的:
colors_t mycolor;
mycolor = blue;
if (mycolor == green) mycolor = red;
使用 enum
声明的枚举类型的值可以隐式转换为整数类型。实际上,这种 enum
的元素总是被内部赋予一个整数数值等效值,可以隐式转换为该数值。如果未另行指定,第一个可能值的整数值等效于 0
,第二个等效于 1
,第三个等效于 2
,依此类推……因此,在上面定义的 colors_t
数据类型中,black
等效于 0
,blue
等效于 1
,green
等效于 2
,依此类推……
可以为枚举类型中的任何可能值指定特定的整数值。如果紧随其后的常量值本身没有给出自己的值,则自动假定其值相同加一。例如:
enum months_t { january=1, february, march, april,
may, june, july, august,
september, october, november, december } y2k;
在这种情况下,枚举类型 months_t
的变量 y2k
可以包含从 january
到 december
的 12 个可能值中的任何一个,这些值等效于 1
到 12
之间的值(而不是 0
到 11
,因为 january
被等同于 1
)。
具有 enum class 的枚举类型
但是,在 C++ 中,可以创建真正的 enum
类型,这些类型既不能隐式转换为 int
,也不能具有 int
类型的枚举值,而是具有枚举类型本身的值,从而保留类型安全性。它们用 enum class
(或 enum struct
)而不是 enum
声明:
enum class Colors
{black, blue, green, cyan, red, purple, yellow, white};
enum class
类型的每个枚举值需要在其类型内进行作用域限定(这实际上对于 enum
类型也是可能的,但仅仅是可选的)。例如:
Colors mycolor;
mycolor = Colors::blue;
if (mycolor == Colors::green) mycolor = Colors::red;
用 enum class
声明的枚举类型还可以更好地控制其底层类型;它可以是任何整数数据类型,例如 char
、short
或 unsigned int
,这实际上用于确定类型的大小。这是通过在枚举类型之后加上冒号和底层类型来指定的。例如:
enum class EyeColor : char {blue, green, brown};
这里,EyeColor
是一个具有 char
(1 字节)大小的独特类型。