类型别名是可用于标识类型的不同名称。在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没有。 因此,虽然typedef的历史更长,并且可能在现有代码中更常见,但是using更通用。
注意,无论是typedef还是using都不能创建新的不同的数据类型。它们只创建现有类型的同义词。 这意味着上面用WORD类型声明的myword类型也可以被认为是unsigned int类型;这并不重要, 因为它们实际上指的是同一种类型。
类型别名可用于减少冗长或令人困惑的类型名的长度,但它们最有用的是作为工具, 从它们使用的底层类型中抽象程序。例如,通过使用int的别名来引用特定类型的形参, 而不是直接使用int,它允许该类型在以后的版本中很容易被long(或其他一些类型)替换, 而不必更改使用它的每个实例。
联合允许将内存的一部分作为不同的数据类型访问。它的声明和使用类似于结构体,但功能完全不同:
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.c mytypes.i mytypes.f |
每个成员都属于不同的数据类型。但由于它们在内存中的相同位置, 因此修改其中一个成员将影响所有成员的值。在它们中存储不同的值是不可能的, 每个值都是独立于其他值的。
联合的用途之一是能够访问值的整体,或作为数组或较小元素的结构。例如:
union mix_t { int l; struct { short hi; short lo; } s; char c[4]; } mix; |
如果我们假设这个程序运行的系统有一个大小为4字节的int类型和一个长度为2字节的short类型, 那么上面定义的联合允许访问相同的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),而在第二种类型中没有。 这将影响访问该类型对象的成员dollers和yen的方式。对于第一种类型的对象(成员是常规联合),它将是:
book1.price.dollars book1.price.yen |
而对于第二种类型的对象(成员是匿名联合),它将是:
book2.dollars book2.yen |
同样,请记住,因为它是一个成员联盟(而不是成员结构),成员dollers和yen实际上共享同一个内存位置, 所以它们不能被用来同时存储两个不同的值。price 可以用dollers或yen确定,但不能同时用两种货币。
枚举类型是使用一组自定义标识符定义的类型,被称为枚举成员,作为可能的值。 这些枚举类型的对象可以将这些枚举中的任何一个枚举成员作为值。
他们的语法是: 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声明的枚举类型的值可以隐式转换为整数类型。 实际上,这种枚举的元素总是在内部分配一个等价的整数数值,它们可以隐式转换为这个数值. 如果没有指定,则与第一个可能值相等的整数值为0,与第二个相等的整数值为1,第三个为2, 依此类推… 因此,在上面定义的数据类型colors_t中,黑色等于0,蓝色等于1,绿色等于2,等等……
可以为枚举类型中的任何值指定特定的整数值。 如果它后面的成员本身没有给出它自己的值,它会自动地被假定为相同的值加1。例如:
enum months_t { january=1, february, march, april, may, june, july, august, september, october, november, december} y2k; |
在本例中,枚举类型months_t的变量y2k可以是从1月到12月的12个可能值中的任何一个, 这些值等价于1到12之间的值(不是0到11之间的值,因为1月被设为1)。
但是,在c++中,可以创建既不能隐式转换为int类型的实际枚举类型,也没有int类型的枚举值, 而是枚举类型本身的枚举值,从而保持类型安全。它们是用enum类(或enum结构体)声明的,而不是用enum:
enum class Colors {black, blue, green, cyan, red, purple, yellow, white}; |
枚举类类型的每个枚举值都需要限定在其类型中(实际上对于枚举类型也可以这样做,但它只是可选的)。例如:
Colors mycolor; mycolor = Colors::blue; if (mycolor == Colors::green) mycolor = Colors::red; |
用enum类声明的枚举类型对其基础类型也有更多的控制;它可以是任何整型数据类型, 如char、short或unsigned int,其本质上用于确定类型的大小。这是由冒号和枚举类型之后的基础类型指定的。例如:
enum class EyeColor : char {blue, green, brown}; |