首页 > 技术文章 > 第3讲——复合类型

xzxl 原文

一、学习新知识

这一讲我们来学些几种普通的复合类型,但是我们要知道,类也是复合类型。

 

首先来看数组,C语言嚼烂的知识了,我们只看新特性。

几种初始化方数组方法:

int arr1[4] = {3,6,8,10};
int arr2[] = {1,2,5,8};
int arr3[4] {1,3,6,9};    //可省略等号 
int arr4[4] {};            //将所有元素设置为零 
4种初始化方法

我们知道声明数组的一种方式是:

typeName arrayName[arraySize];

其中,表达式arraySize指定元素数目,它必须是整型常数或const值,也可以是常量表达式,即其中所有的值在编译时都是已知的。也就是说,arraySize不能是变量,因为变量的值是在程序运行时设置的。

但使用new运算符将避开这种限制。

假设要编写一个程序,它是否需要数组取决于运行时用户提供的信息。如果通过声明来创建数组,则在程序被编译时将为它分配内存空间,不管程序最终是否使用数组,数组都在那里,它占用了内存。

静态联编:在编译时给数组分配内存,即意味着数组是在编译时加入到程序中的;

动态联编:在运行阶段选择是否创建数组并且可以选择数组的长度。

使用new来创建动态数组:

//创建动态数组只需将数组的元素类型和元素数目告诉new即可
//必须在类型名后加上方括号,其中包含元素数目 
int *p = new int[10];	//创建一个包含10个int元素的数组 

new运算符返回第一个元素的地址,在这个例子中,该地址被赋给指针p。

p是指向一个int(数组第一个元素)的指针。我们需要跟踪内存块中的元素个数,也就是说,由于编译器不能对p是指向10个整数中的第一个这种情况进行跟踪,因此编写程序时,必须让程序跟踪元素的数目。

使用动态数组:

#include <iostream>
using namespace std;

int main()
{
	double *p = new double [3];
	p[0] = 0.2;
	p[1] = 0.5;
	p[2] = 0.8;
	cout<<"p[1] is "<<p[1]<<".
";
	p = p + 1;
	cout<<"Now p[0] is "<<p[0]<<" and p[1] is "<<p[1]<<".
";
	p = p - 1;
	delete [] p;		//释放new分配的内存 
	return 0;
}

运行结果为:

看完数组,再来看看结构。

结构是一种比数据更灵活的数据格式,因为同一个结构可以存储多种类型的数据,从而能够将数据的表示合并到一起。

我们要知道,结构也是C++OOP堡垒(类)的基石,学习有关结构的知识将使我们离C++的核心OOP更近。

 

结构是用户定义的类型,而结构声明定义了这种类型的数据属性。定义了类型后,便可以创建这种类型的变量。因此,创建结构包括两步:①定义结构描述;②按描述创建结构变量。

来看一段创建结构的程序:

#include <iostream>
using namespace std;
//结构声明 
struct student{
	char name[20];    //name成员是一个字符数组,可以初始化为一个字符串
	int score;      //将结构成员score看作还是int类型的变量
}; 

int main()
{
	student stu1 = {"liuyu",98};		//创建结构类型student的变量stu1 
	student stu2  {"zhouning",18};	
	cout<<stu1.name<<"的得分是"<<stu1.score<<".
"; 
	cout<<stu2.name<<"的得分是"<<stu2.score<<".
"; 
}

哈哈,上面这段程序只是结构的基本操作啊,我们来段猛的:

#include <iostream>
using namespace std;
//结构声明 
struct student{
	char name[20];
	int score;
}stu2; 		//同时定义结构和创建结构变量 

int main()
{
	student stu1 = {"liuyu",98};	 
	stu2 = stu1;		//进行结构间的赋值	
	cout<<stu1.name<<"的得分是"<<stu1.score<<".
"; 
	cout<<stu2.name<<"的得分是"<<stu2.score<<".
"; 	//输出和上面一模一样 
}

这些特征和我们学C时的结构差不多,但C++结构拥有更多的特性。例如,C++结构除了成员变量之外,还可以有成员函数。但这类高级特性往往被用于类中,而不是结构中。

 

【枚举】

 之前学java也看过枚举了,当时没看懂,今天一定要弄懂!!!

C++的enum工具提供了另一种创建符号常量的方式,这种方式可以代替const。它还允许定义新类型,但必须按严格的限制进行。

使用enum的句法与使用结构相似,请看下面的语句:

enum spectrum{red,orange,yellow,green,blue,violet,indigo,ultraviolet};

这条语句完成两项工作:

  1. 让spectrum成为新类型的名称;spectrum被称为枚举(enumeration),就像struct变量被称为结构一样。
  2. 将red、orange、yellow等作为符号常量,它们将对应整数值0~7。这些常量叫作枚举量(enumerator)。

我们要知道,在默认情况下,将整数值赋给枚举量,第一个枚举量的值为0,第二个枚举量的值为1,依次类推。可以通过显式地指定整数值来覆盖默认值。

由上面的语句我们知道spectrum为枚举名,那么我们可以用枚举名来声明这种类型的变量:

specrtum band;     //band是spectrum类型的变量

下面来看枚举变量的特性:

enum spectrum{red,orange,yellow,green,blue,violet,indigo,ultraviolet};
specrtum band; 
//在不进行强制类型转换的情况下,只能讲定义枚举时使用的枚举量赋给这种枚举的变量
//即band只有8个可能的值 
band = blue;
band = 2000;    //错误,2000不是一个枚举量
//枚举只定义了赋值运算符
band = orange;
band = orange + red;    //错误
//枚举量是整型,可被提升为int类型,但int类型不能自动转换为枚举类型    
band = 3;            //错误,虽然3对应的枚举量为green 
band = green;        //正确 
int color = blue;    //spectrum type promoted to int 
color = 3 + red;    //red被提升为int类型 
//如果int值有效,则可以通过强制类型转换,将它赋给枚举变量
band = spectrum(3);    //3在0-7之间
View Code

我们可以了解到枚举的规则相当严格,所以枚举更常被用来定义相关的符号常量,而不是新类型。

比如,我们可以用枚举来定义switch语句中使用的符号常量。

如果我们只使用常量,我们可以省略枚举类型的名称:

enum {red,orange,yellow,green,blue,violet,indigo,ultraviolet};

我们除了像上面那样由系统帮我们设置枚举量的值,我们也可以自己进行设置:

enum bits{one = 1,two = 2,four = 4,eight =8};
//只定义其中一些枚举量的值
enum bigstep{first,second = 0,third};	//first在默认情况下为0,可以创建多个值相同的枚举量,没有初始化的枚举量值比前面的大1 

二、温故而知新

【编写一条语句,显示float数组ideas中的第二个元素的值。】

cout <<(float)ideas[1]<<endl; 

【声明一个char数组,将其初始化为字符串“cheeseburger”。】

char a[20]="cheeseburger";

【设计一个描述鱼的结构声明。结构中应当包括品种、重量(整数,单位为盎司)和长度(英寸、包括小数)。】

struct fish{
  string pinzhong;     //品种名
  int angsi;	          //重量,盎司,整数
  double yingcun;	     //长度,英寸,浮点数
};

【声明一个问题3中定义的结构的变量,并对它进行初始化。】

fish liyu={"鲤鱼",5,2.2}; 

fish liyu;  
liyu.pinzhong = "鲤鱼";  
liyu.angsi = 5;  
liyu.yingcun = 2.2; 

【用enum定义一个名为Response的类型,他包括Yes,No和Maybe等枚举量,其中Yes的值为1,No为0,Maybe为2。】

enmu Response{Yes=1,No=0,Maybe=2};

【编写一段代码,要求用户输入一个正整数,然后创建一个动态的int数组,其中包含的元素数目等于用户输入的值。】

unsigned int a;  
cin >> a;  
int *b=new int[a];

【下面的代码是否有效?如果有效,他将打印出什么结果?】

有效。他会打印储存这个字符串常量的内存地址。

 

【编写一段代码,给问题3中描述的结构动态分配内存,再读取该结构的成员的值。】

fish *a=new fish;  
cout<<"依次输入鱼类的名字,重量(盎司,整数),长度(英寸,小数),以回车为结束。
"  
cin >> (*a).string;  
cin >> a->angsi;  
cin >> a->yingcun;  

三、做做习题

1.编写一个C++程序,如下述输出示例所示的那样请求并显示信息:

What is your first name? Betty Sue

What is your last name? Yewe

What letter grade do you deserve? B

What is your age? 22

Name: Yewe, Betty Sue

Grade: C

Age: 22

注:该程序应接受的名字包含多个单词。另外,程序将向下调整成绩,即向上调整一个字母。假设用户请求A、B或C,所以不必担心D和F之间的空档。

答:代码:

#include<iostream>
 
int main()
{
using namespace std;
char first[20], last[20];    //声明first name和last name
char grade;    //声明成绩变量grade
int age;    //声明年龄变量age
cout << "What is your first name? ";
cin.getline(first, 20);    //读取用户输入的一行字符给first
cout << "What is your last name? ";
cin.getline(last, 20);    //读取用户输入的一行字符给last
cout << "What letter grade do you deserve? ";
cin >> grade;    //读取用户输入的grade
cout << "What is your age? ";
cin >> age;    //读取用户输入的年龄
cout << "Name: " << last << ", " << first << endl;    //显示名字
cout << "Grade: " << char((int)grade + 1) << endl;    //将用户输入的grade转为int类,然后+1就是下调成绩,再转为char类显示
cout << "Age: " << age << endl;    //显示用户输入的年龄
cin.get();    //和下面起到暂停作用
cin.get();
return 0;
}
View Code

2.编写一个程序,它要求用户首先输入其名,然后输入其姓;然后程序使用一个逗号和空格将姓和名组合起来,并存储和显示组合结果。请使用char数组和头文件cstring中的函数。下面是程序运行时的情形:

Enter your first name: Flip

Enter your last name: Fleming

Here's the information in a single string: Fleming, Flip

答:代码:

#include<iostream>
#include<cstring>
 
int main()
{
using namespace std;
char first[20], last[20];    //创建字符串first和last
char middle[3] = ", ";    //创建用于连接名字的间隔符,和空格
cout << "Enter your first name: ";
cin.getline(first, 20);    //要求用户输入名
cout << "Enter your last name: ";
cin.getline(last, 20);    //要求用户输入姓
 
const int a=strlen(first);    //让常量a等于first的长度
const int b = strlen(last);    //让常量b等于last的长度
char *name = new char[43];    //创建指针,用于将first和last还有中间的间隔符组合到一起
 
strcpy_s(name, b + 1, last);    //将last的内容复制到指针name(所指向地址的字符串)中,注意,长度需要给空字符留一个位置
name = name + b;    //指针偏移,偏移长度为last的长度,这样的话,就将空字符覆写掉了
strcpy_s(name, 3, middle);    //将middle的内容复制到指针name中,同样留一个空字符位置
name = name + 2;    //偏移指针,覆写空字符
strcpy_s(name,a+1, first);    //将first内容复制到指针name中
name = name - b - 2;    //将指针偏移回原来的位置
cout << "Here's the information in a single string: " << name << endl;    //输出指针指向的地址
system("pause");    //用2个cin.get()就不会显示最后的 请按任意键继续. . . 但问题是,好像也没法复制输出的内容了
return 0;
}
View Code

3.结构CandyBar包含3个成员。第一个成员存储了糖块的品牌;第二个成员存储糖块的重量(可以有小数);第三个成员存储了糖块的卡路里含量(整数)。请编写一个程序,声明这个结构,创建一个名为snack的CandyBar变量,并将其成员分别初始化为“Mocha Munch”、2.3和350。初始化应在声明snack时进行。最后,程序显示snack变量的内容。

答:代码:

#include<iostream>
#include<string>
 
struct CandyBar
{
std::string pinpai;    //品牌
double weight;    //重量
int kaluli;    //卡路里
};
 
int main()
{
using namespace std;
CandyBar snack = { "Mocha Munch",2.3,350 };    //声明并初始化snack
cout << snack.pinpai << "的重量为" << snack.weight << ",卡路里含量为:" << snack.kaluli << endl;
system("Pause");
return 0;
}
View Code

4.结构CandyBar包含3个成员,如编程练习3所示。请编写一个程序,创建一个包含3个元素的CandyBar数组,并将它们初始化为所选择的值,然后显示每个结构的内容。

答:代码:

#include<iostream>
#include<string>
 
struct CandyBar
{
std::string pinpai;    //品牌
double weight;    //重量
int kaluli;    //卡路里
};
 
int main()
{
using namespace std;
CandyBar tang[3];
tang[0] = { "大白兔奶糖",3.3,200 };
tang[1] = { "小白兔奶糖",2.2,150 };
tang[2] = { "巧克力糖",4.2,300 };
cout << tang[0].pinpai << "的重量为" << tang[0].weight << ",卡路里含量为:" << tang[0].kaluli << endl;
cout << tang[1].pinpai << "的重量为" << tang[1].weight << ",卡路里含量为:" << tang[1].kaluli << endl;
cout << tang[2].pinpai << "的重量为" << tang[2].weight << ",卡路里含量为:" << tang[2].kaluli << endl;
system("Pause");
return 0;
}
View Code

5.William Wingate从事比萨饼分析服务。对于每个比萨饼,他都需要记录下列信息:

● 披萨饼公司的名称,可以由多个单词组成。

● 披萨饼的直径。

● 披萨饼的重量。

请设计一个能够储存这些信息的结构,并编写一个使用这种结构变量的程序。程序将请求用户输入上述信息,然后显示这些信息。请使用cin(或它的方法)和cout。

答:代码:

#include<iostream>
#include<string>
 
struct pisa
{
std::string gs;    //公司
double zj;    //直径
int zl;    //重量
};
 
int main()
{
using namespace std;
cout << "这里可以帮你储存披萨饼的相关信息。
请输入披萨饼的公司名称: ";
pisa a;
getline(cin, a.gs);
cout << "请输入披萨饼的直径(英寸): ";
cin >> a.zj;
cout << "请输入披萨饼的重量(克): ";
cin >> a.zl;
cout << a.gs << "的披萨饼,直径为" << a.zj << "英寸,重量为" << a.zl << "克。" << endl;
system("Pause");
return 0;
}
View Code

6.完成编程练习5,但使用new来为结构分配内存,而不是声明一个结构变量。另外,让程序在请求输入比萨饼公司名称之前输入比萨饼的直径。

答:代码:

#include<iostream>
#include<string>
 
struct pisa
{
std::string gs;    //公司
double zj;    //直径
int zl;    //重量
};
 
int main()
{
using namespace std;
pisa *a = new pisa;    //使用动态结构数组
cout << "这里可以帮你储存披萨饼的相关信息。" << endl;
cout << "请输入披萨饼的直径(英寸): ";
cin >> (*a).zj;
cout<<"请输入披萨饼的公司名称: ";
cin.get();    //读取掉换行符
getline(cin, a->gs);
cout << "请输入披萨饼的重量(克): ";
cin >> a->zl;
cout << a->gs << "的披萨饼,直径为" << a->zj << "英寸,重量为" << a->zl << "克。" << endl;
system("Pause");
return 0;
}
View Code

7.完成编程练习4,但使用new来动态分配数组,而不是声明一个包含3个元素的CandyBar数组。

答:代码:

#include<iostream>
#include<string>
 
struct CandyBar
{
std::string pinpai;    //品牌
double weight;    //重量
int kaluli;    //卡路里
};
 
int main()
{
using namespace std;
CandyBar *tang=new CandyBar[3];
tang[0] = { "大白兔奶糖",3.3,200 };
tang[1] = { "小白兔奶糖",2.2,150 };
tang[2] = { "巧克力糖",4.2,300 };
cout << tang[0].pinpai << "的重量为" << tang[0].weight << ",卡路里含量为:" << tang[0].kaluli << endl;
cout << tang[1].pinpai << "的重量为" << tang[1].weight << ",卡路里含量为:" << tang[1].kaluli << endl;
cout << tang[2].pinpai << "的重量为" << tang[2].weight << ",卡路里含量为:" << tang[2].kaluli << endl;
system("Pause");
return 0;
}
View Code

8.编写一个程序,让用户输入三次40码跑的成绩(如果您愿意,也可让用户输入40米跑的成绩),并显示次数和平均成绩。请使用一个array对象来存储数据(如果编译不支持array类,请使用数组)。

答:代码:

#include<iostream>
#include<array>
 
int main()
{
using namespace std;
const int b = 3;
array<double, b>a;
cout << "请输入三次四十米跑的成绩。
请输入第一次的成绩(秒): ";
cin >> a[0];
cout << "请输入第二次的成绩(秒): ";
cin >> a[1];
cout << "请输入第三次的成绩(秒): ";
cin >> a[2];
cout << "" << b << "次的成绩分别为:" << a[0] << "秒、" << a[1] << "秒、" << a[2] << "秒。
平均成绩为:" << (a[0] + a[1] + a[2]) / 3 << "秒。" << endl;
system("pause");
return 0;
}
View Code

推荐阅读