从零学习C++第五章:复合数据的描述——构造数据类型(一)

5.1 枚举类型——自定义值集的数据描述

  • 如果用基本数据类型来表示一些形式与之相同而性质不同的数据,会造成程序易读性下降,正确性难以保证。为解决该问题,设计者可以使用自己定义值集的数据类型——枚举类型,枚举类型的操作受一定的约束。

 

5.1.1 枚举类型的举例

  • 枚举类型(enumeration type是用户自定义的一种简单数据类型,在定义一个枚举类型时,需要列出其值集中的每一个值。
  • 枚举类型的定义格式:
    • enum <枚举类型名> (<枚举值表>);
      • 枚举类型名为标识符,是枚举类型的名称,可省略
      • 枚举值表为用逗号隔开的若干个枚举值,为整型符号常量,默认0、1、2...
  • 枚举类型变量的定义格式:
    • <枚举类型名> <变量表>;
  • bool类型是C++预定义的枚举类型
  • 使用枚举类型可以提高程序的易读性和保证程序的正确性

 

5.1.2 枚举类型的操作

 

赋值操作

  • 可对枚举类型进行赋值操作,但不同枚举类型之间不能相互赋值
  • 可把一个枚举值赋值给一个整型变量,可通过强制类型转换把一个整型值赋给某个枚举类型的变量

 

比较运算

  • 可对枚举类型进行比较运算,运算前先转换为相应的整型值,然后在进行比较运算,运算结果为bool类型

 

算术运算

  • 运算时,枚举值将转换为对应的整型值,然后进行算术运算,运算结果为算术型

 

输入/输出

  • 不能对枚举类型的值直接进行输入,但可直接输出枚举类型的值,输出时,枚举类型转为int型

 

使用枚举类型有利于提高程序的易读性和保证程序的正确性

Enum.h
#ifndefPROGRAMEDESIGN_ENUM_H
#definePROGRAMEDESIGN_ENUM_H

enumDay{SUN,MON,TUE,WED,THU,FRI,SAT};

classEnum{

public:
	static Day function1(intn);
	static void function2(Dayday);

};


#endif//PROGRAMEDESIGN_ENUM_H

Enum.cpp
#include"Enum.h"
#include<iostream>

DayEnum::function1(intn){
Dayd;
if(n<0||n>6){
	std::cout<<""<<std::endl;
	return(Day)-1;
}else{
	switch(n){
		case0:d=SUN;
			break;
		case1:d=MON;
			break;
		case2:d=TUE;
			break;
		case3:d=WED;
			break;
		case4:d=THU;
			break;
		case5:d=FRI;
			break;
		case6:d=SAT;
			break;
	}
	return d;
	}
}

voidEnum::function2(Dayday){
switch(day){
	caseSUN:
	std::cout<<"SUN"<<std::endl;
		break;
	caseMON:
	std::cout<<"MON:"<<std::endl;
		break;
	caseTUE:
	std::cout<<"TUE"<<std::endl;
		break;
	caseWED:
	std::cout<<"WED"<<std::endl;
		break;
	caseTHU:
	std::cout<<"THU"<<std::endl;
		break;
	caseFRI:
	std::cout<<"FRI"<<std::endl;
		break;
	caseSAT:
	std::cout<<"SAT"<<std::endl;
		break;
	}
}

Enum.cpp
#include"Enum.h"
#include<iostream>

DayEnum::function1(intn){
Dayd;
if(n<0||n>6){
	std::cout<<""<<std::endl;
	return(Day)-1;
}else{
	switch(n){
		case0:d=SUN;
			break;
		case1:d=MON;
			break;
		case2:d=TUE;
			break;
		case3:d=WED;
			break;
		case4:d=THU;
			break;
		case5:d=FRI;
			break;
		case6:d=SAT;
			break;

    }
}

main.cpp
int main(){

	Enum::function2(Enum::function1(2));

	return 0;
}



5.2 数组类型——由多个同类型元素构成的复合数据描述

  • 数组类型(array type)是一种由固定多个同类型的具有一定次序关系的元素所构成的复合数据类型,分为一维数组、二维数组、多维数组

 

5.2.1 一维数组类型——线性复合数据的描述

  • 一维数组通常用于表示由固定多个同类型的具有线性次序关系的数据多构成的复合数据,复合数据统称为线性表(linear list)
  • 一维数组的定义
    • C格式:<元素类型>  <一维数组变量名> [<元素个数>];
    • C++格式:typedef <元素类型><一维数组类型名>[<元素个数>];
    • 以下两种方式等价:
      • int a[10];
      • typedef int A[10];  A a;
    • 注意:C++中,数组类型的元素个数是固定的,在程序执行中不能改变
  • 一维数组变量的初始化
    • 可在定义时或定义后对其进行初始化
    • 定义时进行初始化,元素个数可省略,避免给出的元素个数与初始值个数的不一致问题
  • 一维数组的操作
    • 访问一维数组的元素:
      • 格式:<一维数组变量名> [<下标>]
      • 注意:元素下标是从0开始的
    • 注意:
      • C++不会检查给定的数组下标的值是否超出了数组元素个数的范围,因为检查数组下标越界需要在程序运行时刻进行,需要开销
      • C++不能对数组进行整体赋值

1、用一维数组求斐波那契数

	int Array::fib(intn){
	inta[n];
	a[0]=a[1]=1;
	for(inti=2;i<n;++i){
		a[i]=a[i-2]+a[i-1];
	}
	return a[n-1];
	}

2、统计某地区在某年中湿度最大和最小的月份
 

void Array::display_month(Monthm){
switch(m){
    case JAN:cout<<"January";
        break;
    case FEB:cout<<"February";
        break;
    case MAR:cout<<"March";
        break;
    case APR:cout<<"April";
        break;
    case MAY:cout<<"May";
        break;
    case JUN:cout<<"June";
        break;
    case JUL:cout<<"July";
        break;
    case AUG:cout<<"August";
        break;
    case SEP:cout<<"September";
        break;
    case OCT:cout<<"October";
        break;
    case NOV:cout<<"November";
        break;
    case DEC:cout<<"December";
        break;
}
cout<<endl;
}

void Array::show_month(double*d){
Month i,min,max;
max=min=JAN;
for(i=FEB;i<=DEC;i=Month(i+1)){
	if(d[i]>d[max]){
		max=i;
	}elsei f(d[i]<d[min]){
		min=i;
	}
}
cout<<"最大湿度的月份是:";
display_month(max);
cout<<"最小湿度的月份是:";
display_month(min);

}

int main(){

doubled[12];
cout<<"输入某年12个月的湿度数据:"<<endl;
for(Monthi=JAN;i<=DEC;i=Month(i+1)){
	cin>>d[i];
}

Array::show_month(d);

return0;
}

  • 一维数组的存储
    • 系统在内存中为其分配连续的存储空间来存储数组元素,sizeof(a)能计算出一维数组a所占的字节数
    • 数组元素a[i]在存储空间中的地址=a的首地址+i*sizeof(int)

a[0]

 

 

 

 

 

 

 

a[7]

a[8]

  • 向函数传递一维数组
    • 传递一维数组时,被调用者要定义两个行参,一个是不带数组大小的一维数组的定义;另一个是数组元素的个数
    • 数组作为函数参数传递时,实际传递的是实参数组在内存中的首地址,函数的行参数组不再另外分配内存空间,而是共享参数组的内存空间
      • 带来的问题:在函数中可以通过行参改变实参的值
  • 一维字符数组——字符串类型的一种实现
    • C++中没有字符串类型,通常用元素类型为char的一维数组(字符数组)来表示字符串类型
    • C++存储字符串时,在字符串的最后一位的后面存储一个字符‘\0’(编码为0的字符),作为结束标记
      • 注意:定义字符串数组时,元素个数比字符串长度多一;在给字符串数组赋值时,记住最后一个字符的后面是\0(比如你定义一个元素个数3的字符型数组数组的长度为2,数组的可用元素个数为2,最后一位元素为\0
      • 字符数组作为参数传递,由于有结束标记,所以不需要其中的字符个数
    • 对字符数组的输入输出
      • 输入:将键盘字符逐个放入字符数组,直到碰到空白符(空格,制表,回车等)为止,然后在最后一个字符的后面加'\0'
      • 输出:逐个把字符数组里的字符输出,直到出现'\0'为止

a[0]

 

 

 

 

 

 

 

a[7]

\0

3、把一个由数字构成的字符串转换成一个整型数

int Array::str_to_int(char*c){

int n=0;

for(int i=0;c[i]!='\0';i++){

n=n*10+(c[i]-'0');//由于数组c中存储的是各位数字对应的ASCII码,把它们减去数字字符'0'(编码)可得到它们的数值

}

return n;

}

4、查找一个字符串(字串)在另一个字符串(主串)中第一次出现的位置

int Array::find_substr(char*str,char*sub_str){

int len,sub_len;

for(len=0;str[len]!='\0';++len);//计算主串长度

for(sub_len=0;sub_str[sub_len]!='\0';++sub_len);//计算子串长度

//cout<<"len:"<<len<<",sub_len:"<<sub_len<<endl;

 

for(inti=0;i<=len-sub_len;++i){

intj=0;

while(j<sub_len&&sub_str[j]==str[i+j]){

j++;

}

if(j==sub_len){

return i;

}

}

return -1;

}

5、从键盘输入一个字符串,然后逆向输出

void Array::print_str(){

char c[100];

int len;

cout<<"输入一串字符:"<<endl;

cin>>c;

for(len=0;c[len]!='\0';len++);

for(int i=0,j=len-1 ; i<j ; i++,j--){

chartemp;

temp=c[i];

c[i]=c[j];

c[j]=temp;

}

cout<<c<<endl;

 

}

 

5.2.2 二维数组类型——二维复合数据的描述

  • 二维数组用于表示由固定多个同类型的,具有行列结构的数据所构成的复合数据
    • 第一维被称为二维数组的行,第二维称为二维数组的列。二维数组的元素由行列确定
    • 也可以把二维数组看作一个一维数组,该一维数组的每个元素又是一个一维数组
  • 二维数组类型的定义
    • C格式:<元素类型><二维数组变量名>[<行数>][<列数>];
    • C++格式:typedef <元素类型> <二维数组类型名> [<行数>][<列数>];
    • 以下三种方式等价:
      • int a [10] [5];
      • typedef int A[10] [5]; A a;
      • typedef int A[5]; A a[10];
  • 二维数组的初始化
    • 可在定义时或定义后对其进行初始化
    • 定义时进行初始化,则数组的行数可省略,其行数由初始值的个数决定
      • int a [ ] [3]={{1,2,3} ,{4,5,6} ,{1,8,9}};// 二维数组a有三行
  • 二维数组的操作
    • 访问二维数组的格式:<二维数组变量名> [<下标1>] [<下标2>]
      • 对二维数组每个元素的访问需要一个二重循环来实现

6、从键盘输入一个N×N的矩阵,把它转置后输出

voidArray::print_matrix(){

constintN=3;

inta[N][N];

inti,j;

cout<<"请输入"<<N<<"x"<<N<<"矩阵:\n";

for(inti=0;i<N;++i){

for(intj=0;j<N;++j){

cin>>a[i][j];

}

}

 

//转置规则

for(i=0;i<N;++i){

for(j=i+1;j<N;++j){

int temp=a[i][j];

a[i][j]=a[j][i];

a[j][i]=temp;

}

}

cout<<"转置后为:\n";

for(i=0;i<N;++i){

for(j=0;j<N;++j){

cout<<a[i][j]<<"⊔";

}

cout<<endl;

}

}
  • 二维数组的存储
    • 系统在内存中为其分配连续的存储空间来存储数组元素,sizeof(a)能计算出二维数组a所占的字节数
    • 计算二维数组x中某个元素的内存地址:addr(x [i] [j]=x的内存首地址+i*列数+j

a[0][0]

a[0][1]

a[0][2]

a[1][0]

a[1][1]

a[1][2]

a[2][0]

...

  • 在带有内存高速缓存(在cpu中)的计算机上,系统会把常用的数据放入cpu的高速缓存,以减少访问内存的数据。如果一个数组过大,缓存区放不下,会把该数组分块放入,新放入的部分把以前的部分替换掉
  • 向函数传递二维数组
    • 调用者要提供两个实参,二维数组变量名和行数;被调用者要定义两个行参,不带数组行数的二维数组定义和行数
    • 在C++中,把一个二维数组传递给某个函数时,传递的也是该二维数组的内存首地址,因此二维数组作为行参必须给出列数
int sum (int x[5] [] ,int lin){ //二维数组作为行参,必须给出列数;lin为行数

int s=0;

for(int i=0;i<lin;i++){

for(int j=0;j<5;j++){

s=s+x[i][j];

}

}

return s;

}
  • 二维数组降为一维数组处理
    • 对一维数组的求和函数
int sum (int x[],int num){

int s=0;

for(int i=0;i<num;i++){

s=s+x[i];

}

return s;

}
  • C++中的多维数组
    • 定义:<元素类型><数组变量名>[<第1维元素个数>][<第二维元素个数>]...[<第n维元素个数>]
    • 对于一个n维的数组,可理解为一个一维数组,其元素个数为n维数组的第一维的大小,元素类型为去掉第一维后的n-1维数组,以此类推

 

5.2.3 数组类型的应用

7、实现矩阵乘法C=A×B

voidArray::print_matrix2(){

 

const int M=2,N=3,T=4;

int a[M][N],b[N][T],c[M][T];

inti,j,k;

 

//输入矩阵A的值

cout<<"请输入矩阵A("<<M<<"x"<<N<<"):\n";

for(i=0;i<M;++i){

for(j=0;j<N;j++){

cin>>a[i][j];

}

}

 

//输入矩阵B的值

cout<<"请输入矩阵B("<<N<<"x"<<T<<"):\n";

for(i=0;i<N;++i){

for(j=0;j<T;j++){

cin>>b[i][j];

}

}

 

//计算矩阵C的值

for(i=0;i<M;i++){

for(j=0;j<T;j++){

c[i][j]=0;

for(k=0;k<N;k++){

c[i][j]=c[i][j]+a[i][k]*b[k][j];

}

}

}

 

//输出矩阵c的值

cout<<"矩阵C("<<M<<"x"<<T<<")为:\n";

for(i=0;i<M;++i){

for(j=0;j<T;j++){

cout<<c[i][j]<<"⊔";

}

cout<<endl;

}

}

 

8、用数组实现求解约瑟夫(Josephus)问题

问题描述:N个小孩围成一圈,从某个小孩开始顺时针报数,报到M的小孩从圈子中离开,然后,从下一个小孩开始重新报数,每报到M,相应的小孩从圈子离开,最后一个离开圈子的小孩为胜者,问胜者是哪个小孩 ?

思路:

1、构建圈子:数组下标表示小孩编号,元素类型为bool,将数组看作循环数组(围成一圈),即数组的最后一个元素(in_circle[N-1])的下一个元素是数组的第一个元素(in_circle[0])

2、报数:从编号为0的小孩开始报数,用变量index表示要报数的小孩的坐标,初始值为N-1(即将报数的前一个小孩的下标)。下一个报数的小孩的下标用index=(index+1)%N来计算。变量count对报数成功进行计数,直到M为止。要使得报数成功,in_circle[index]应为true。用变量num_of_children_remained表示圈中剩下的小孩数目,初始值为N。

 

void Array::josephus(int N,int M){

//N个小孩,报到M离开

 

//将该数组看作循环数组,即数组的最后一个元素in_circle(N-1)下一个元素是in_circle(0)

bool in_circle[N];

//index表示要报数的小孩的下标,初始值为N-1

int num_of_children_remained,index;

 

//初始化数组in_circle

for(int i=0;i<N;++i){

in_circle[i]=true;

}

 

//开始报数

index=N-1;//从编号为0的小孩开始报数,index为前一个小孩的下标

num_of_children_remained=N;//报数前圈子中小孩的数量

while(num_of_children_remained>1){

int count=0;

while(count<M){//对成功的报数进行计数

index=(index+1)%N;//计算要报数的小孩的编号

if(in_circle[index]){

count++;//如果编号为index的小孩在圈子里,则该报数为成功的报数

}

}

in_circle[index]=false;//小孩离开圈子

num_of_children_remained--;//圈中小孩数减1

}

 

//找到最后一个小孩

for(index=0;index<N;index++){

if(in_circle[index]){

break;

}

}

cout<<"The winner isNo."<<index<<"\n";

}

从键盘输入N个整数,从小到大排序,输出排序结果

选择排序法(selection sort:从n个数中找到最大者,与第n个数(坐标n-1)交换位置;然后从剩下n-1个数中,找到最大数,与第n-1个数(坐标n-2)交换位置;一直到剩下的数只有一个为止

	void Array::sel_sort(intx[],int n){
	for(;n>1;n--){//基于数的个数进行循环,每次减少一个数
		inti_max=0;//假设第0个元素最大
		for(inti=1;i<n;++i){//循环找前n个数中的最大元素
			if(x[i]>x[i_max]){
				i_max=i;//修改i_max的值,使其一直为最大元素的下标
				}
			}
		//交换x[i_max]和x[n-1]的值
		int temp=x[i_max];
		x[i_max]=x[n-1];
		x[n-1]=temp;
		}
	
	}

int main(){
int a[10];
cout<<"输入10个整数:"<<endl;
for(int i=0;i<10;++i){
	cin>>a[i];
	}

Array::sel_sort(a,10);

for(inti=0;i<10;++i){//输出排序结果
	cout<<a[i]<<"";
	}
cout<<endl;

return 0;
}

快速排序法(quick sort:将数组元素分为A、B、C三个部分,其中B中只有一个元素(pivot),A中的所有元素都小于pivot,C中的所有元素都大于或等于pivot。然后对A和C两个部分的元素采用同样的策略进行划分,一直到所有部分中只剩下一个元素为止。体现了“分而治之”的程序设计思想,适合用递归方法实现。

 

//快速排序
//对数组元素在first和last之间(包括first和last)的元素进行排序
voidArray::quick_sort(intx[],intfirst,intlast){
if(first<last){
	int split_point;
	split_point=split(x,first,last);
	quick_sort(x,first,split_point-1);
	quick_sort(x,split_point+1,last);
	}

}




//split函数把数组x中的下标在first和last之间的元素加工(交换位置)成三分:[first,split_point-1],split_point,[split_point+1,last]

//使得第一部分的元素小于x[split_point],第三部分的元素大于等于x[split_point]

int Array::split(intx[],intfirst,intlast){

//取x[first]的值作为pivot,split_point初始为first

int split_point,pivot;

pivot=x[first];

split_point=first;

//从first+1的位置开始,每个元素(位置由unknown指示)逐个与pivot比较

for(intunknown=first+1;unknown<=last;unknown++){

//当元素小于pivot时,把该元素与split_point+1位置上的元素(它大于或等于pivot,或者就是正在比较的那个元素)交换位置,

//并把split_point加1,以保持split_point位置之前的元素(除x[first]外)小于pivot

if(x[unknown]<pivot){

split_point++;

//交换x[split_point]和x[unknown]的值

intt=x[split_point];

x[split_point]=x[unknown];

x[unknown]=t;

}

}

//交换x[first]和x[split_point]的值,数组x划分完毕

x[first]=x[split_point];

x[split_point]=pivot;

returnsplit_point;

}

补充:

  1. 快排效果最优,pivot应取元素大小的中间值
  2. 通常情况下,快排比其它排序算法快,但是快排采用了递归函数调用,所以排序元素较少时,函数调用所需开销使其效率降低。所以可将快排与其它算法结合使用,设置排序元素小于多少时,在使用快排

 

冒泡排序法(bubble sort:对N个元素,从第一个元素开始,用每个元素与它的后一个元素进行比较,如果前者大,则交换前、后两个元素的位置,直到这N个元素两两比完,N个元素中最后一位为最大者。然后进行下一趟比较,直到没有发生交换,则表明元素排序完毕。

 

void Array::bubble_sort(int x[],int n){

bool exchange;

while(n>1){

exchange=false;

for(int i=0;i<n-1;++i){

if(x[i]>x[i+1]){

int temp=x[i+1];

x[i+1]=x[i];

x[i]=temp;

exchange=true;

}

}

if(!exchange){

break;

}

n--;

}

}

 


5.3 结构类型——若干个属性构成的复合数据描述 

为了解决对由不同数据类型所构成的复合数据的描述问题,C++提供了结构类型。

5.3.1 结构类型的定义

  • 结构类型(struceure type用于表示由固定多个、类型可以不同的元素所构成的复合数据(这些元素在逻辑上没有次序关系)
  • 结构类型中的元素称为结构的成员(member)
  • 结构类型的定义
    • struct <结构类型名> {<成员表>};
  • 结构类型变量的定义
    • <结构类型名> <变量名表>;
  • 结构类型的变量也可以在定义结构类型的同时定义,这时结构类型名可以省略
  • 结构类型变量的初始化
    • 在定义结构类型时不能对其成员进行初始化
      • 类型不是程序运行时刻的实体,它们不占有空间,对其初始化没有意义

 

5.3.2 结构类型的操作

  1. 访问结构的成员
    • 格式:<结构类型变量>.<成员名>
      • 点(.) :成员访问操作符
    • 不同结构类型的成员的名字可以相同
    • 结构成员名的作用域为相应的结构类型定义,称为结构作用域
    • 为提高程序的易读性,结构名应避免与实体名相同
  2. 结构数据的赋值
    • 对结构类型的数据可以整体赋值
    • 不同结构类型之间不能相互赋值
  3. 向函数传递结构数据
    • 传递时,行参定义为结构类型实参为结构变量的名字
    • 结构类型默认参数传递方式是值传递,结构类型数据较大时,参数传递的效率不高
    • 结构类型也可作为返回值类型
  4. 结构数据的存储
    • 结构类型的变量在内存中占有一块连续的存储空间,各个元素依据定义次序存储在这块内存空间中
    • sizeof( )计算结构体所占用的内存大小
    • 在C++实现中,为了提高计算机指令对结构及其成员的访问效率,在为结构类型的变量分配内存空间时,对结构成员所占空间按某种方式进行地址对齐,使得结构成员之间可能存在空隙。
      • 为了提高结构成员的存储效率,可把不需要对齐的成员放在一起,编译器一般能让用户自己设置对齐方式

 

5.3.3 结构类型的应用

1、名表及其查找

线性查找(linear search:从第一个元素开始,逐个与要查找的内容进行比较,直到结束。

折半查找(或二分查找,binary search:首先将要查找的值与中间元素进行比较,若相等则找到,否则,若大于中间位置上的元素,则在后半部分进行查找;若小于中间位置上的元素,则在前半部分进行查找。在前半部分或后半部分进行查找时,仍然采用折半查找。

int Array::binary_search(charkey[],TableItemt[],intnum_of_items){

int index,first,last;

first=0;

last=num_of_items;

while(first<=last){

index=(first+last)/2;

int r=strcmp(key,t[index].name);

if(r==0){

return index;

}elseif(r>0){

first=index+1;

}else{

last=index-1;

}

}

return-1;

}

在最坏情况下,折半查找只要log2N(N为元素个数)次就能得到结果,比顺序查找少得多


5.4 联合类型——用一种类型表示多种类型的数据

C++提供一种能够表示多种类型数据的数据类型——联合类型(union type,它是一种用户自定义类型

 

5.4.1 联合类型的定义与操作

  • 联合类型的定义
    • 语法上,与结构类型相似,把struct关键字换成union
    • 语义上,联合类型的成员是互斥的,在程序中不能同时使用。在程序中把它们作为不同的类型来使用,而不会同时把它作为几种类型使用
  • 联合类型的存储
    • 联合类型的成员共用一块内存空间,空间大小为其最大成员所需要的内存空间大小
      • 当给联合类型的变量赋于一个某种类型的值之后,如果以另一种类型来使用这个值,一般得不到原来的值
  • 向函数传递联合类型
    • 可以对联合类型数据的整体进行赋值
    • 可以把联合类型的数据传给函数和作为函数的返回值
      • 联合类型的赋值(包括参数传递)是按其占有的整个内存空间中的内容进行复制,而不是按某个成员来赋值
  • 联合类型除了可以用一种类型表示多种类型的数据以外,还可以实现多个数据共享内存空间,起到节省内存的作用。
    • 对于一些大型的数组变量,如果对它们的使用分布在程序的各个阶段(不是同时使用),则可把这些数组变量用一个联合类型描述。在程序的不同阶段,通过联合类型的不同成员来使用这些数组变量中的数据。

 

5.4.2 联合类型的应用

1、从键盘输入一组图形数据(线段、矩形、圆),然后输出相应的图形

struct Line{

double x1,y1,x2,y2;

};

 

struct Rectangle{

double left,top,right,bottom;

};

 

struct Circle{

double x,y,r;

};

 

union Figure{

Line line;

Rectangle rectangle;

Circle circle;

};

 

enum FigureShape{

LINE,RECTANGLE,CIRCLE

};

 

//变体记录类型(识别是何图形)

struct TaggedFigure{

FigureShape shape;

Figure figure;

};


void Union::draw(){
int count;
for(count=0;count<MAX_NUM_OF_FIGURES;++count){
	int shape;
	do{
		cout<<"请输入图形的种类(0:线段,1:矩形,2:圆,-1:结束):"<<endl;
		cin>>shape;
		}while(shape<-1||shape>2);
	if(shape==-1){
		break;
	}else{
		switch(shape){
		case0://线段
			figures[count].shape=LINE;
			cout<<"请输入线段的起点和终点坐标(x1,x2,y1,y2):";
			cin>>figures[count].figure.line.x1>>figures[count].figure.line.x2>>figures[count].figure.line.y1>>figures[count].figure.line.y2;
			break;
		case1://矩形
			figures[count].shape=RECTANGLE;
			cout<<"请输入矩形的左上角和右下角坐标(left,top,right,bottom):";
			cin>>figures[count].figure.rectangle.left>>figures[count].figure.rectangle.top>>figures[count].figure.rectangle.right>>figures[count].figure.rectangle.bottom;
			break;
		case2://圆形
			figures[count].shape=CIRCLE;
			cout<<"请输入圆的圆心坐标和半径(x,y,r):";
			cin>>figures[count].figure.circle.x>>figures[count].figure.circle.y>>figures[count].figure.circle.r;
			break;
		}
	}
}
//输出所有图形
for(inti=0;i<count;++i){
	switch(figures[i].shape){
	case LINE:
		draw_line(figures[i].figure.line);
		break;
	case RECTANGLE:
		draw_rectangle(figures[i].figure.rectangle);
		break;
	case CIRCLE:
		draw_circle(figures[i].figure.circle);
		break;
	}
	}
}

 

基于抽象基类、继承以及虚函数的动态绑定也可解决该问题


参考:《程序设计教程:用C++语言编程》 陈家骏,郑滔

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值