0
点赞
收藏
分享

微信扫一扫

C++PrimerPlus 第四章 复合类型 - 4.4 结构简介

WikongGuan 2022-03-24 阅读 21

C++PrimerPlus 第四章 复合类型 - 4.4 结构简介

4.4 结构简介

假设要存储有关篮球运动员的信息,则可能需要存储他(她)的姓名、工资、身高、体重、平均得分、命中率、助攻次数等。希望有一种数据格式可以将所有这些信息存储在一个单元中。数组不能完成这项任务,因为虽然数组可以存储多个元素,但所有元素的类型必须相同。也就是说,一个数组可以存储20个int,另一个数组可以存储10个float,但同一个数组不能在一些元素中存储int,在另一些元素中存储float。

C++中的结构的可以满足要求(存储篮球运动员的信息)。结构是一种比数组更灵活的数据格式,因为同一个结构可以存储多种类型的数据,这使得能够将有关篮球运动员的信息放在一个结构中,从而将数据的表示合并到一起。如果要跟踪整个球队,则可以使用结构数组。结构也是C++OOP堡垒(类)的基石。学习有关结构的知识将使我们离C++的核心OOP更近。

结构是用户定义的类型,而结构声明定义了这种类型的数据属性。定义了类型后,便可以创建这种类型的变量。因此创建结构包括两步。首先,定义结构描述——它描述并标记了能够存储在结构中的各种数据类型。然后按描述创建结构变量(结构数据对象)。

例如,假设Bloataire公司要创建一种类型来描述其生产线上充气产品的成员。具体地说,这种类型应存储产品名称、容量(单位为立方英尺)和售价。下面的结构描述能够满足这些要求:

struct inflatable	//structure declaration
{
	char name[20];
	float volume;
	double price;
};

关键字struct表明,这些代码定义的是一个结构的布局。标识符inflatable是这种数据格式的名称,因此新类型的名称为inflatable。这样,便可以像创建char或int类型的变量那样创建inflatable类型的变量了。接下来的大括号中包含的是结构存储的数据类型的列表,其中每个列表项都是一条声明语句。这个例子使用了一个适合用于存储字符串的char数组、一个float和一个double。列表中的每一项都被称为结构成员,因此infatable结构有3个成员(参见下图)。总之,结构定义指出了新类型(这里是inflatable)的特征。
在这里插入图片描述
定义结构后,便可以创建这种类型的变量了:

inflatable hat;				//hat is a structure variable of type inflatable
inflatable woopie_cushion;	//type inflatable variable
inflatable mainframe;		//type inflatable variable

如果您熟悉C语言中的结构,则可能已经注意到了,C++允许在声明结构变量时省略关键字struct:

struct inflatable goose;		//keyword struct required in C
inflatable vincent;				//keyword struct not required in C++

在C++中,结构标记的用法与基本类型名相同。这种变化强调的是,结构声明定义了一种新类型。在C++中,省略struct不会出错。

由于hat的类型为inflatable,因此可以使用成员运算符(.)来访问各个成员。例如,hat.volume指的是结构的volume成员,hat.price指的是price成员。同样,vincent.price是vincent变量的price成员。总之,通过成员名能够访问结构的成员,就像通过索引能够访问数组的元素一样。由于price成员被声明为double类型,因此hat.price和vincent.price相当于是double类型的变量,可以像使用常规double变量那样来使用它们。总之,hat是一个结构,而hat.price是一个double变量。顺便说一句,访问类成员函数(如cin.getline())的方式是从访问结构成员变量(如vincent.price)的方式衍生而来的。

4.4.1 在程序中使用结构

介绍结构的主要特征之后,下面在一个使用结构的程序中使用这些概念。程序清单4.11说明了有关结构的这些问题,还演示了如何初始化结构。

程序清单4.11 structur.cpp

//structur.cpp -- a simple structure
#include<iostream>
struct inflatable	//structure declaration
{
	char name[20];
	float volume;
	double price;
};

int main()
{
	using namespace std;
	inflatable guest =
	{
		"Glorious Gloria",	//name value
		1.88,				//volume value
		29.99				//price value
	};	//guest is a structure variable of type inflatable
	//It's initialized to the indicated values
	inflatable pal =
	{
		"Audacious Arthur",
		3.12,
		32.99
	};//pal is a second variable of type inflatable
	//NOTE: some implementations require using
	//static inflatable guest =

	cout << "Expand your guest list with " << guest.name;
	cout << " and " << pal.name << "!\n";
	//pal.name is the name member of the pal variable
	cout << "You can have both for $";
	cout << guest.price + pal.price << "!\n";

	return 0;
}

下面是该程序的输出:
Expand your guest list with Glorious Gloria and Audacious Arthur!
You can have both for $62.98!

接下来,请注意初始化方式:

inflatable guest =
{
	"Glorious Gloria",	//name value
	1.88,				//volume value
	29.99				//price value
};

和数组一样,使用由逗号分隔值列表,并将这些值用花括号括起。在该程序中,每个值占一行,但也可以将它们全部放在同一行中。只是应用逗号将它们分开:

inflatable duck = {“Daphne”, 0.12, 9.98};

可以将结构的每个成员都初始化为适当类型的数据。例如,name成员是一个字符数组,因此可以将其初始化为一个字符串。

可将每个结构成员看作是相应类型的变量。因此,pal.price是一个double变量,而pal.name是一个char数组。当程序使用cout显示pal.name时,将把该成员显示为字符串。另外,由于pal.name是一个字符数组,因此可以用下标来访问其中的各个字符。例如,pal.name[0]是字符A。不过pal[0]没有意义,因为pal是一个结构,而不是数组。

4.4.2 C++11结构初始化

与数组一样,C++11也支持将列表初始化用于结构,且等号(=)是可选的:

inflatable duck{"Daphne", 0.12, 9.98};		//can omit the = in C++11

其次,如果大括号内未包含任何东西,各个成员都将被设置为零。例如,下面的声明导致mayor.volume和mayor.price被设置为零,且mayor.name的每个字节都被设置为零:

inflatable mayor{};

最后,不允许缩窄转换。

4.4.3 结构可以将string类作为成员吗

可以将成员name指定为string对象而不是字符数组吗?即可以像下面这样声明结构吗?

#include<string>
struct inflatable	//structure declaration
{
	std::string name;
	float volume;
	double price;
};

答案是肯定的,只要您使用的编译器支持对以string对象作为成员的结构进行初始化。

一定要让结构定义能够访问名称空间std。为此,可以将编译指令using移到结构定义之前;也可以像前面那样,将name的类型声明为std::string。

4.4.4 其他结构属性

C++使用户定义的类型与内置类型尽可能相似。例如,可以将结构作为参数传递给函数,也可以让函数返回一个结构。另外,还可以使用赋值运算符(=)将结构赋给另一个同类型的结构,这样结构中每个成员都将设置为另一个结构中相应成员的值,即使成员是数组。这种赋值被称为成员赋值(memberwise assignment),将在第7章讨论函数时再介绍如何传递和返回结构。下面简要地介绍一下结构赋值,程序清单4.12是一个这样的示例。

程序清单4.12 assgn_st.cpp

//assgn_st.cpp -- assigning structures
#include<iostream>
struct inflatable
{
	char name[20];
	float volume;
	double price;
};
int main()
{
	using namespace std;
	inflatable bouquet =
	{
		"sunflowers",
		0.20,
		12.49
	};
	inflatable choice;
	cout << "bouquet: " << bouquet.name << " for $";
	cout << bouquet.price << endl;

	choice = bouquet;	//assign one structure to another
	cout << "choice: " << choice.name << " for $";
	cout << choice.price << endl;

	return 0;
}

下面是该程序的输出:
bouquet: sunflowers for $12.49
choice: sunflowers for $12.49

从中可以看出,成员赋值是有效的,因为choice结构的成员值与bouquet结构中存储的值相同。

可以同时完成定义结构和创建结构变量的工作。为此,只需将变量名放在结束括号的后面即可:

struct perks
{
	int key_number;
	char car[12];
}mr_smith, ms_jones;		//two perks variables

甚至可以初始化以这种方式创建的变量:

struct perks
{
	int key_number;
	char car[12];
}mr_glitz = 
{
	7,			//value for mr_glitz.key_number member
	"Packard"	//value for mr_glitz.car member
};

然而,将结构定义和变量声明分开,可以使程序更易于阅读和理解。

还可以声明没有名称的结构类型,方法是省略名称,同时定义一种结构类型和一个这种类型的变量:

struct		//no tag
{
	int x;	//2 members
	int y;
}position;	//a structure variable

这样将创建一个名为position的结构变量。可以使用成员运算符来访问它的成员(如position.x),但这种类型没有名称,因此以后无法创建这种类型的变量。本书将不使用这种形式的结构。

除了C++程序可以使用结构标记作为类型名称外,C结构具有到目前为止讨论的C++结构的所有特性(C++11特性除外),但C++结构的特性更多。例如,与C结构不同,C++结构除了成员变量之外,还可以有成员函数。但这些高级特性通常被用于类中,而不是结构中,因此将在讨论类的时候(从第10章开始)介绍它们。

4.4.5 结构数组

inflatable结构包含一个数组(name)。也可以创建元素为结构的数组,方法和创建基本类型数组完全相同。例如,要创建一个包含100个inflatable结构的数组,可以这样做:

inflatable gifts[100];	//array of 100 inflatable structures

这样,gifts将是一个inflatable数组,其中的每个元素(如gifts[0]或gifts[99])都是inflatable对象,可以与成员运算符一起使用:

cin >> gifts[0].volume;					//use volume member of first struct
cout << gifts[99].price << endl;		//display price member of last struct

记住,gifts本身是一个数组,而不是结构,因此像gifts.price这样的表述是无效的。

要初始化结构数组,可以结合使用初始化数组的规则(用逗号分隔每个元素的值,并将这些值用花括号括起)和初始化结构的规则(用逗号分隔每个成员的值,并将这些值用花括号括起)。由于数组中的每个元素都是结构,因此可以使用结构初始化的方式来提供它的值。因此,最终结果为一个被括在花括号中、用逗号分隔的值列表,其中每个值本身又是一个被括在花括号中、用逗号分隔的值列表:

inflatable guests[2] =				//initializing an array of structs
{
	{"Bambi", 0.5, 21.99},			//first structure in array
	{"Godzilla", 2000, 565.99}		//next structure in array
};

可以按自己喜欢的方式来格式化它们。例如,两个初始化位于同一行,而每个结构成员的初始化各占一行。

程序清单4.13是一个使用结构数组的简短示例。由于guests是一个inflatable数组,因此guests[0]的类型为inflatable,可以使用它和句点运算符来访问相应inflatable结构的成员。

程序清单4.13 arrstruc.cpp

//arrstruc.cpp -- an array of structures
#include<iostream>
struct inflatable
{
	char name[20];
	float volume;
	double price;
};

int main()
{
	using namespace std;
	inflatable guests[2] =				//initializing an array of structs
	{
		{"Bambi", 0.5, 21.99},			//first structure in array
		{"Godzilla", 2000, 565.99}		//next structure in array
	};

	cout << "The guests " << guests[0].name << " and " << guests[1].name
		<< "\nhave a combined volume of "
		<< guests[0].volume + guests[1].volume << " cubic feet.\n";
	return 0;
}

下面是该程序的输出:
The guests Bambi and Godzilla
have a combined volume of 2000.5 cubic feet.

4.4.6 结构中的位字段

与C语言一样,C++也允许指定占用特定位数的结构成员,这使得创建与某个硬件设备上的寄存器对应的数据结构非常方便。字段的类型应为整数或枚举(稍后将介绍),接下来是冒号,冒号后面是一个数字,它指定了使用的位数。可以使用没有名称的字段来提供间距。每个成员都被称为位字段(bit field)。下面是一个例子:

struct torgle_register
{
	unsigned int SN : 4;		//4 bits for SN value
	unsigned int : 4;			//4 bits unused
	bool goodIn : 1;			//valid input(1 bit)
	bool goodTorgle : 1;		//successful torgling
};

可以像通常那样初始化这些字段,还可以使用标准的结构表示法来访问位字段:

torgle_register tr = { 14,true,false };
...
if(tr.goodIn)		//if statement covered in Chapter 6
...

位字段通常用在低级编程中。一般来说,可以使用整型和附录E介绍的按位运算符来代替这种方式。

举报

相关推荐

0 条评论