【C++习题】 《C++语言程序设计第四版》第1~6章 编程习题记录

【2_27】编写一段程序,运行时向用户提问“你考了多少分?(0~100)”,接受输入后判断其等级并显示出来。

判断依据如下:等级={优秀 (90~100分);良好 (80~89分);一般 (60~79分);不及格 (0~59分);}

要求:必须用switch语句实现。

代码:

int Code2_27()
{
    int scores = 0;
    cout << "你考了多少分?" << endl;
    cin >> scores;
    int caseScores = scores / 10;                         //用“整除”将分数的逻辑范围转化为case常量
    switch (caseScores)
    {
        case(10):case(9):   cout << "你的等级为:优秀";     //多个case对应一个表达式写法
        break;
	case(8):	    cout << "你的等级为:良好"; 
        break;
	case(7):case(6):    cout << "你的等级为:一般"; 
        break;
	case(5):case(4):case(3):case(2):case(1):case(0):cout << "你的等级为:不合格";
        break;
	default:cout << "请检查分数是否在此范围内!" << endl;
	break;
    }
    return scores;
}

需要注意的点:

1.switch语句里的case只能跟常量,而判断分数明显是逻辑判断,因此需要将逻辑值转化为常量

2.switch里多个case对应相同表达式的时写法


 

【2_28】实现一个简单的菜单程序,运行时显示"Menu: A(dd) D(elete) S(ort) Q(uit),Select one:"提示用户输入,A 表示增加,D 表示删除,S 表示排序,Q 表示退出,输入为 A、D、S 时分别提示"数据已经增加、删除、排序。"直到输入为 Q 时程序结束。

要求:必须使用 Switch 语句。

代码:

int Code2_28()
{

    char userInput;
    cout << "Menu: A(add) D(delete) S(sort) Q(quit),Select one:" << endl;
    cin >> userInput;
    while (userInput !='Q')
    {
        switch (userInput)	
        {
	    case 'A':cout << "数据已经增加...." << endl; break;
            case 'D':cout << "数据已经删除..." << endl; break;
	    case 'S':cout << "数据已经排序..." << endl; break;
	    default:cout << "未找到此功能,请重新输入...." << endl; break;
	}
	cin >> userInput;
	continue;
    }
    cout << "程序即将退出...." << endl;
    return 0;
}

需要注意的点:

1.switch对应的常量不仅只有整型常量,还有实型常量和字符型常量等,因此可直接匹配字符'A'。


 

【2_29】用穷举法找出1~100的质数并显示出来,并分别用while、do-while、for循环语句实现.。

代码:

int Code_2_29()
{
	//质数定义:不能被它本身和1以外的数整除


	/*
	*  description:用while循环求1~100之间的质数
	*  parm: null
	*  return: null
	*/ 
	int number_1 = 1;
	while (number_1 >=1&& number_1 <=100)
	{
		int i = 1;
		int counts = 0;
		while (i<= number_1)
		{
			if (number_1%i == 0)                    //整除的意思也就是求余数为0
			{
				counts++;		        //counts是余数为0的记次数
			}
			i++;				        //i从1递增到number本身
		}
		if (counts==2)				        //如果是两次整除就是1和本身,说明是质数
		{
			cout << number_1 << "是质数" << endl;
		}
		number_1++;
	}

	cout << "**************分割线***************" << endl;


	/*
	*  description:用do...while循环求1~100之间的质数
	*  parm: null
	*  return: null
	*/
	int number_2 = 1;
	do
	{
		int i = 1;
		int counts = 0;
		do
		{
			if (number_2%i==0)
			{
				counts++;
			}
			i++;
		} while (i<= number_2);

		if (counts==2)
		{
			cout << number_2 << "是质数" << endl;
		}
		number_2++;
	} while (number_2 >=1&& number_2 <=100);
	
	cout << "**************分割线***************" << endl;


	/*
	*  description:用for循环求1~100之间的质数
	*  parm: null
	*  return: null
	*/
	for (int number_3 = 1; number_3 <= 100; number_3++)
	{
		int counts = 0;
		for (int i = 1; i <= number_3; i++)
		{
			if (number_3%i==0)
			{
				counts++;
			}
		}
		if (counts==2)
		{
			cout << number_3 << "是质数"<<endl;
		}

	}
	return 0;
}

需要注意的点:

1.整除的意思也就是求余数为0

2.本质是双层循环嵌套:第一层循环穷举1~100,第二层循环穷举从1~自身


 

【2_32】在程序中定义一个整型变量,赋以 1~100 的值,要求用户猜这个数,比较两个数的大小,把结果提示给用户,直到猜对为止。

代码:

#include<cmath>
#include<time.h>
int Code_2_32()
{
	srand((unsigned)time(NULL));		        //置随机数种子
	int number = rand()%100+1;			//在1-100内随机取一个数
	int guessNum = -1;
	cout << "请猜一猜这个数字是多少?(1~100)" << endl;
	cin >> guessNum;
	while (guessNum != number)
	{
		if (guessNum>number)
		{
			cout << "输入的值偏大,请重新输入.." << endl;
		}
		else
		{
			cout << "输入的值偏小,请重新输入..." << endl;
		}
		cin >> guessNum;
	}
	cout << "恭喜你,猜对了!" << endl;
	return 0;
}

需要注意的点:

1.置随机数种子是必要的,否则每次随机都是固定的,是假随机

2.注意从1~100取一个随机数的写法,用到了求余+1操作(不加1是在0~99取随机数)


 

【2_34】口袋中有红、黄、蓝、自、黑种颜色的球若干个。每次从口袋中取出个不同颜色的球,问有多少种取法?

代码:

#include<iomanip>  
int Code_2_34()
{
	//本质上是三层循环嵌套问题,每个循环体循环5次,判断i,j,k是否相同,不同就是三种不同的颜色
	enum  Color
	{
		Red=1,Yellow,Blue,White,Black			//枚举值设为从1开始
	};
	//Color nowColor;				        //测试枚举用
	int totals = 0;					        //totals代表一共有多少次取法
	//第一层循环i
	for (int i = 1; i <= 5; i++)
	{
		//第二层循环j
		for (int j = 1; j <= 5; j++)
		{
			//第三层循环k
			for (int k = 1; k <= 5; k++)
			{
				//判断i,j,k是否相同,如果不同,则说明是取到了三种不同颜色
				if (i!=k && k!=j && j!=i)
				{
					//totals计数+1
					totals++;
					cout << setw(3) << totals;

					//接下来判断和输出i,j,k三种不同颜色各是什么
					//只需要用switch分别匹配当前i,j,k对应的枚举值即可

					//首先判断i是什么颜色
					switch (i)
					{
					case Red:cout <<setw(8)<< "Red"; break;
					case Yellow:cout << setw(8) << "Yellow"; break;
					case Blue:cout << setw(8) << "Blue"; break;
					case White:cout << setw(8) << "White"; break;
					case Black:cout << setw(8) << "Black"; break;
					}


					//其次判断j是什么颜色
					switch (j)
					{
					case Red:cout << setw(8) << "Red"; break;
					case Yellow:cout << setw(8) << "Yellow"; break;
					case Blue:cout << setw(8) << "Blue"; break;
					case White:cout << setw(8) << "White"; break;
					case Black:cout << setw(8) << "Black"; break;
					}


					//最后判断k是什么颜色
					switch (k)
					{
					case Red:cout << setw(8) << "Red"; break;
					case Yellow:cout << setw(8) << "Yellow"; break;
					case Blue:cout << setw(8) << "Blue"; break;
					case White:cout << setw(8) << "White"; break;
					case Black:cout << setw(8) << "Black"; break;
					}
					cout << endl;  //换行
				}
			}
		}

	}
	cout << "答:共有" << totals << "种取法." << endl;
	return 0;
}

需要注意的点:

1.问题可抽象为从5个球中取三次,三次都是不同的颜色。因此,取三次进一步抽象为3层循环,每层循环执行5次,遍历所有可能的结果,从中找出三层循环都取到不一样的颜色这种情况,再分别匹配三层循环中i,j,k分别是什么颜色即可。

2.匹配可用switch分别匹配i,j,k的序号,再输出枚举类型中对应的颜色(变量类型名)即可。


【例3_4】寻找并输出11~999之间的数m,它满足m、m2和m3均为回文数。

注:回文数是指其各位数字左右对称的整数。例如:121,676,94249等。满足上述条件的数如m=11 ,m2 = 121 ,m3 = 1331。

代码:

/*
*  description: 输入一个数字,返回其反序数
*  parm: 传入int型整数—number
*  return: 返回int型反序数—reverseOrder
*/
int reverse_Num(int number)
{
	//求余%10:获得当前的最低位也就是个位
	//整除/10:舍弃当前的最低位也就是个位
	int modResult = 0;				        //存放求余后的数
	int dividRrsult = number;				//存放整除后的数
	int reverseOrder = 0;					//存放反序后的数

	while (dividRrsult>0)					//只要整除后的数仍然大于0,说明至少是两位数,继续整除
	{
		modResult = dividRrsult % 10;			//每次反复求余10,求出当前最低位个位
		reverseOrder = reverseOrder * 10 + modResult;	//因为每次都舍弃了一个10位,所以每循环一次就乘10一次补上,再加上最新求得最低位构成反序数
		dividRrsult = dividRrsult / 10;			//舍弃最低位个位
	}
	
	return reverseOrder;
}	
int Exam_3_4()
{
	for (int number = 11; number <= 999; number++)
	{
		//对number反序
		int reverseNum1 = reverse_Num(number);
		//对number平方反序
		int reverseNum2 = reverse_Num(number*number);
		//对number的三次方反序
		int reverseNum3 = reverse_Num(number*number*number);

		//判断number其本身、平方、三次方是否都和反序相同
		if (number==reverseNum1 && number*number==reverseNum2 && number*number*number==reverseNum3)
		{
			cout << number << "是回文数" << endl;
		}
	}
	return 0;
}

需要注意的点: 

1.要求回文数,核心是求其每个数的反序数。因此,此题解决关键就在于求反序。

2.反序数求解是用到了两个符号:【%求余】和【/整除】,%求余10可以求出当前的最低位(个位),/整除10可以舍弃当前的最低位(个位)。这里需要注意的是,求反序数时,每整除一次10,就缩小了10倍,因此在反序时每次都是*10返回去再加上此时%10的个位即可(上述代码17行处)


【例3_6】投骰子的随机游戏。

游戏规则是:每个骰子有六面,点数分别为1、2、3、4、5、6。每轮投两次骰子,第一轮如果和数为7或11则为胜,游戏结束;和数为2、3或12则为负,游戏结束;和数为其它值则将此值作为自己的点数,继续第二轮、第三轮…直到某轮的和数等于点数则取胜,若在此前出现和数为7则为负。

代码:

#include <cmath>
#include<ctime>
int Exam_3_6()
{
	bool winFlag;
	srand(time(0));

	//第一轮
	int point1 = rand() % 6 + 1;
	int point2 = rand() % 6 + 1;
	int sum = point1 + point2;
	
	if (sum == 7 || sum == 11)
	{
		cout << "第一轮sum为" << sum <<endl;
		cout << "You are Win" << endl;
	}
	else if (sum == 2 || sum == 3 || sum == 12)
	{
		cout << "第一轮sum为" << sum << endl;
		cout << "You are lose" << endl;
	}
	else
	{
		//开始第N轮,直到出现和数等于点数或和数为7时停止.
		int roundNum;
		bool endFlag=false;
		for (roundNum = 2; sum!=roundNum ; roundNum++)
		{
			int point1 = rand() % 6 + 1;
			int point2 = rand() % 6 + 1;
			int sum = point1 + point2;
			if (sum==7)
			{
				cout << "第" << roundNum << "轮和数为7"<<endl;
				cout << "You are lose" << endl;
				endFlag = true;
				break;
			}
			
		}
		if (!endFlag)
		{
			cout << "第"<<roundNum<<"轮时,和数为"<<sum << endl;
			cout << "You are Win" << endl;
		}

	}
	return 0;
}

需要注意的点: 

1.随机函数rand()%number是产生的[0~number-1]范围的随机数,若想产生[1~number]范围只需要在后面加1。

2.rand()操作是假随机操作,应该用srand(seed)置随机数种子,否则在每次程序运行开始时种子都会默认为1;


【3_13】用递归的方法编写函数求Fibonacci级数,公式为:

                                                      Fn=Fn-1+Fn-2(n>2),F1=F2=1

代码:

int fib(int n)
 {
	 int answer = 0;
	 if (n==1||n==2)
	 {
		 answer = 1;
		 return answer;
	 }
	 else
	 {
		 answer = fib(n - 1) + fib(n - 2);        //递归调用:调用自身
		 return answer;
	 }
 }
 int Code_3_13()
 {
	 int n;
	 cout << "请输入n的值..." << endl;
	 cin >> n;
	 cout<<fib(n);
	 return 0;
 }

需要注意的点: 

1.递归调用自身,需要缕清思路。


 

【3_15】编写递归函数getPower计算x的y次方,在同一个程序中正对整型和实型实现两个重载的函数:

                                                     int getPower(int x,inty);                       //整形形式,当y<0时,返回0
                                                            double getPower(double x,int y);        //实型形式

代码:

#include<cmath>
 //重载函数-int型函数
 int getPower(int x, int y)
 {
	 int answer;
	 if (y < 0)
	 {
		 return 0;
	 }
	 if (y==1)
	 {
		 answer = x;
		 return answer;
	 }
	 else
	 {
		 //x的y次方=x*(x的y-1次方)
		 answer = x * getPower(x, y - 1);	//递归调用:不断降低y的次数,直至为1后返回
		 cout << "int型被调用了" << endl;
		 return answer;
	 }

 }
 //重载函数-double型函数
 double getPower(double x, int y)
 {
	 double answer;
	 if (y < 0)
	 {
		 return 0;
	 }
	 if (y==1)
	 {
		 answer = x;
		 return answer;
	 }
	 else
	 {
		 //x的y次方=x*(x的y-1次方)
		 answer = x * getPower(x, y - 1);	//递归调用:不断降低y的次数,直至为1后返回
		 cout << "double型被调用了" << endl;
		 return answer;
	 }
 }
 int Code_3_15()
 {
	 int x1,y1;
	 double x2;int y2;

	 cout << "请输入整形x1,y1的值:"<<endl;
	 cin >> x1;
	 cin >> y1;
	 cout<<x1<<"的"<<y1<<"次方为:"<<getPower(x1, y1)<<endl;
	 cout << "重载函数测试:double型" << endl;
	 cout << "请输入double型x2,int型y2的值:" << endl;
	 cin >> x2;
	 cin >> y2;
	 cout << x2 << "的" << y2 << "次方为:" << getPower(x2, y2) << endl;
	 return 0;
	 
 }

需要注意的点: 

1.函数的递归调用,x的y次幂分解为x* (y-1)次幂,形成递归。

2.重载函数即相同函数名称函数但是变量类型不同,编译器会根据传入的参数自动确定。


 

【4_10】设计一个用于人事管理的“人员”类,由于考虑到通用性,这里只抽象出所有类型人员都有的属性:编号、性别、出生日期、身份证号等。其中“出生日期”声明为一个“日期”类内嵌子对象,用成员函数实现对人员信息的录入和显示,要求包括构造函数和析构函数、复制构造函数、内联成员函数、带默认形参的成员函数、类的组合。

代码:

//Date类的定义
 class Date 
 {
 public:
	 Date(int yy=1970,int mm=1, int dd=1);		//Date类成员函数之构造函数的声明(#默认形参的成员函数)
	 Date(Date &d);					//Date类成员函数之复制构造函数的声明
	 void setDate();				//Date类成员函数之设置出生日期函数的声明
	 void showDate();				//Date类成员函数之显示出生日期函数的声明
	 ~Date(){}					//Date类成员函数之析构函数的声明

//Date类私有数据成员
 private:
	 int Year, Month, Day;		
 };

 //Date类成员函数之构造函数的实现
 //注意:使用inline关键字变为内联构造函数,当然也可不用inline关键字直接在类内定义实现
 inline Date::Date(int yy, int mm, int dd)
 {
	 Year = yy;
	 Month = mm;
	 Day = dd;
 }

 //Date类成员函数之复制构造函数的实现
 Date::Date(Date &d)
 {
	 Year = d.Year;
	 Month = d.Month;
	 Day = d.Day;
 }

 //Date类成员函数之设置出生日期函数的实现
 void Date::setDate()
 {
	 cout << "请分别输入出生年、月、日:" << endl;
	 cin >> Year >> Month >> Day;
 }

 //Date类成员函数之显示出生日期函数的实现
 void Date::showDate()
 {
	 cout << Year << "年" << Month << "月" << Day << "日";
 }

 
 //Person类的定义
 class Person
 {
 public:
	 Person(int firstNum, char firstSex, Date firstDate, string firstID);		//Person组合类成员函数之构造函数的声明(内嵌子类对象Date)
	 Person(Person&p);                                                              //Person组合类成员函数之复制构造函数的声明
	 void inputPerson();								//Person组合类成员函数之录入人员函数的声明
	 void showPerson();								//Person组合类成员函数之显示人员函数的声明
	 ~Person(){}									//Person组合类成员函数之析构函数的声明
 
//Person组合类数据成员,注意:其数据成员包含Date类对象birthDate
 private:
	 int number; char sex; Date birthDate; string id;
 };

 //Person组合类成员函数之构造函数的实现(内嵌子类对象Date)
 //注意:因为是组合类,这里的构造函数使用了初始化列表(:冒号及之后的)来初始化
 Person::Person(int firstNum, char firstSex, Date firstDate, string firstID) :number(firstNum), sex(firstSex), birthDate(firstDate), id(firstID)
 {
	 cout << "调用构造函数初始化完毕!" << endl;
 }

 //Person组合类成员函数之复制构造函数的实现
 //注意:因为是组合类,这里的复制构造函数使用了初始化列表(:冒号及之后的)来实现对象的复制
 Person::Person(Person &p) :number(p.number), sex(p.sex), birthDate(p.birthDate), id(p.id)
 {
	 cout << "调用复制构造函数!" << endl;
 }

 //Person组合类成员函数之录入人员函数的实现
 void Person::inputPerson()
 {
	 cout << "**************分隔符strat**************" << endl;
	 cout << "开始录入人员:" << endl;
	 cout << "请输入该人员的编号:" << endl;
	 cin >> number;
	 cout << "请输入该人员的性别(m 或 f):" << endl;
	 cin >> sex;
	 birthDate.setDate();		                                        //调用Date类对象birthdate成员函数setDate
	 cout << "请输入该人员的身份证号:" << endl;
	 cin >> id;
	 cout << "信息录入完毕!" << endl;
	 cout << "**************分隔符end**************" << endl;
 }

 //Person组合类成员函数之显示人员函数的实现
 void Person::showPerson()
 {
	 cout << "**************分隔符strat**************" << endl;
	 cout << "#编号:" << number << endl;
	 cout << "#性别:" << sex << endl;
	 cout << "#出生日期:", birthDate.showDate(), cout << endl;		//调用Date类对象birthdate的成员函数showDate
	 cout << "#身份证号:" << id << endl;
	 cout << "信息显示完毕!" << endl;
	 cout << "**************分隔符end**************" << endl;
 }


 int Code_4_10()
 {
	 //Date类对象的实例化和初始化
	 Date firstDate(1970, 1, 1);	

	 /*Person类对象的实例化和初始化
	 注意:这里Person类内嵌了Date类,因此实例化了一个对象firstDate使得Person类对象myPerson1能够初始化
	 在这里Date类相当于一个自定义类型,首先你得有一个Date类对象才能赋值初始化*/
	 Person myPerson1(0, 'm', firstDate, "370000197001010000");		

	 myPerson1.showPerson();		//显示初始化后的myPerson1人员信息
	 myPerson1.inputPerson();		//录入myPerson1人员信息
	 myPerson1.showPerson();		//录入人员信息后再次显示myPerson1刚录入的信息
	 Person myPerson2 = myPerson1;		//调用复制构造函数将对象myPerson1复制到对象myPerson2
	 myPerson2.showPerson();		//显示myPerson2的人员信息
	 return 0;
 }
习题4_10 - 运行结果

需要注意的点: 

1.组合类的构造函数和复制构造函数和普通类没什么区别,只不过在其实现形式上用了初始化列表(:冒号),其本质还是没有变化,都是进行了初始化赋值操作,只是效率有所提高。

2.内联成员函数可以直接写在类定义内,也可以写在类外,但要加关键字inline

3.组合类调用构造函数初始化,若有参数,且参数之一是内嵌类对象,则内嵌类对象必须先实例化和初始化,如上所示myPerson1的实例化和初始化中内嵌对象Date类的对象firstDate,其Date firstDate(1970, 1, 1)先进行了实例化,再传入对象myPerson1,充当其初始化的参数。


【5_11】在一个文件中定义一个全局变量n,主函数main(),在另一个文件中定义函数fn1(),在main()中对n赋值,再调用fn1(),在fn1()中也对n赋值,显示n最后的值。

代码:

//Antoher.h
#pragma once
#ifndef _ANOTHER_H
#define _ANOTHER_H
int fn1();
#endif // !_ANOTHER_H



//Another.cpp
#include"pch.h"
#include<iostream>

using namespace std;
int fn1()
{
    extern int n;
    n=99;
    cout<<n<<endl;
    return 0;
}


//main.cpp
#include"pch.h"
#include<iostream>
using namespace std;
int n;
extern int fn1();
int main()
{
    n=0;
    cout<<n<<endl;
    fn1();
    return 0;
}

需要注意的点: 

1.一般而言,我们要把一些声明放在.h文件中

2.需要调用其他文件的函数时,有很多方法,可以直接添加cpp文件声明extern也可以用#include头文件的形式


 

【5_13】 定义类X、Y、Z,函数h(X*),满足:类X有私有成员i,Y的成员函数g(X*)是X的友元函数,实现对X的成员i加1,类Z是类X的友元类,其成员函数f(X*)实现对X的成员i加5,函数h(X*)是X的友元函数,实现对X的成员i加10。

代码:

class X;							//X类的前向声明

 //Y类的定义
 class Y
 {
 public:
	 void g(X* xClass);					//声明Y类的成员函数g(X*)
	 Y() {}							//Y类的内联构造函数
	 ~Y() {}						//Y类的内联复制构造函数
 };

 //X类的定义
 class X
 {
 public:
	 //friend class Y;			//声明类Z为类X的友元类
	 friend void Y::g(X* xClass);		//声明Y的成员函数g(X*)为X类的友元函数
	 friend class Z;			//声明类Z为类X的友元类
	 friend void h(X*);			//声明普通函数h(X*)是类X的友元函数
	 X(int ii);				//X类的构造函数声明
	 X(X &x);				//X类的复制构造函数声明
	 ~X(){}					//X类的析构函数声明
 private:
	 int i;

 };

 //X类的构造函数实现
 X::X(int ii)				
 {
	 i = ii;
	 cout << "i的值:" << i << endl;
 }

 //X类的复制构造函数实现
 X::X(X &x)
 {
	 i = x.i;
 }

 //Y类的成员函数g(X*)实现
 void Y::g(X* xClass)
 {
	 xClass->i= xClass->i+1;			        //修改X::i的值+1
	 cout << "i的值:" << xClass->i<<endl;
 }

 //定义Z类
 class Z
 {
 public:
	 Z() {}							//Z类的内联构造函数
	 void f(X*);						//声明Z类的成员函数f(X*)
	 ~Z() {}						//Z类的析构函数
 };

 //Z类的成员函数f(X*)的实现
 void Z::f(X* xClass)
 {
	 xClass->i = xClass->i + 5;				//修改X::i的值+5
	 cout << "i的值:" << xClass->i << endl;
 }

 //普通函数h(X*)的定义
 void h(X*xClass)
 {
	 xClass->i = xClass->i + 10;				//修改X::i的值+10
	 cout << "i的值:" << xClass->i << endl;
 }
 

 int Code_5_13()
 {
	 X xTest(0);						//X类的实例化且通过构造函数初始化
	 Y yTest;						//Y类的实例化对象yTest
	 Z zTest;						//Z类的实例化对象zTest
	 yTest.g(&xTest);					//对象yTest调用xTest函数
	 zTest.f(&xTest);					//对象zTest调用zTest函数
	 h(&xTest);						//函数h(X*)调用
	 return 0;
 }

需要注意的点: 

1.注意友元函数和友元类对本类私有成员的访问方式:可以参数传递直接实例化X类xclass,也可指针访问(X*xclass)

2.类的声明和定义不同,声明了你就写用不牵涉类内部细节的语句,只有定义了才可以用牵涉类细节的语句。如此题中前向声明了X类,Y类中可以用X*xclass,但不能说是用X::X的成员函数,因为那只是一个声明

posted @ 2020-07-17 17:27  小宋和小宋子  阅读(380)  评论(0)    收藏  举报