C++学习笔记(自用)

这篇博客详细记录了C++实现Score类的过程,包括输入处理、构造函数重载、内联函数、成绩排序及调试中遇到的问题与解决办法。内容涵盖了类的设计、成员函数的访问控制以及内存优化等知识点。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

作业要求:

创建一个Score类,完成以下功能:

连续输入多位学生的float成绩(成绩 = 科目A成绩 + 科目B成绩 + 科目C成绩);

学生数目可以有用户自定义(默认为2个,最多为100个);

显示每位同学的每科成绩和平均分;

显示每门科目的平均成绩;

对每门成绩进行排序由高到低显示;

对整个文件进行打包。

要求包括了输入部分,显示每位学生成绩和平均分的部分,求平均的部分,求科目平均分的部分,每门成绩排序的部分。

#include <iostream>
#include<string>
using namespace std;
class Score {
public:
	string Name[100] = {};
//构造函数与析构函数
	Score() {
		times = 2;
		cout << "————————构造函数————————" << endl;
	}
	Score(int times1) {
		times = times1;
		cout << "————————构造函数————————" << endl;
	}
	~Score() {
		cout << "————————析构函数————————" << endl;
	}
//在类声明中只给出成员函数的原型,而将成员函数的定义放在类的外部。
	inline void InputGrade();
	inline void ShowGrade();
	inline void ShowAvgGrade();
	inline void paixu();

private:
	float Grade[100][3];
	int times;
};

构造函数的重载

类中可以定义多个构造函数,以便为对象提供不同的初始化的方法,供用户选用,这些构造函数具有相同的名字,而参数的个数或参数的类型不相同,这称为构造函数的重载。

题目要求默认2名学生,可自定义学生通过构造函数重载可以简单实现。

内联函数

在函数名前冠以关键字inline,该函数就被声明为内联函数。每当程序中出现对该函数的调用时,C++编译器使用函数体中的代码插入到调用该函数的语句之处,同时使用实参代替形参,以便在程序运行时不再进行函数调用。引入内联函数主要是为了消除调用函数时的系统开销,以提高运行速度。

在类中,使用inline定义内联函数时,必须将类的声明和内联成员函数的定义都放在同一个文件(或同一个头文件)中,否则编译时无法进行代码置换。

inline void Score::InputGrade() {
	for (int i = 0; i < times; i++) {
		cout <<"请输入学生姓名" << endl;
		cin >> Name[i];
		cout << "请输入科目A成绩:" << endl;
		cin >> Grade[i][0];
		cout << "请输入科目B成绩:" << endl;
		cin >> Grade[i][1];
		cout << "请输入科目C成绩:" << endl;
		cin >> Grade[i][2];
	}
}

通过for语句循环输入学生姓名与成绩。

成绩数组Grade在Score中为private属性,在本次作业中没有用到类的继承,类的成员函数可以访问类中其他成员。

若存在类的继承,访问规则如下图:

inline void Score::ShowGrade() {
//通过for循环输出存入Name和Grade中的内容。
	for (int i = 0; i < times; i++) {
		cout << "姓名:"<<Name[i]<<endl;	
		cout << "科目A的成绩:"<<Grade[i][0] << "科目B的成绩:" << Grade[i][1] << "科目C的成绩:" << Grade[i][2] << endl;
	}
}

inline void Score::ShowAvgGrade() {
//求每个学生的平均成绩
	float AvgStudent[100] = {0};
	for (int i = 0; i < times; i++) {
		AvgStudent[i] = AvgStudent[i] + Grade[i][0] + Grade[i][1] + Grade[i][2];
		AvgStudent[i] = AvgStudent[i] / 3;
	}
//求每科的平均成绩
	float AvgsubjectA = 0;
	float AvgsubjectB = 0;
	float AvgsubjectC = 0;
	for (int i = 0; i < times; i++) {
		AvgsubjectA = AvgsubjectA + Grade[i][0];
		AvgsubjectB = AvgsubjectB + Grade[i][1];
		AvgsubjectC = AvgsubjectC + Grade[i][2];
	}
	AvgsubjectA = AvgsubjectA / times;
	AvgsubjectB = AvgsubjectB / times;
	AvgsubjectC = AvgsubjectC / times;
//循环输出
	for (int i = 0; i < times; i++) {
		cout << "姓名:" << Name[i] << ";平均分:" << AvgStudent[i] << endl;
	}
	cout << "学科A的平均成绩:" << AvgsubjectA << ";学科B的平均成绩:" << AvgsubjectB << ";学科C的平均成绩:" << AvgsubjectC << endl;
}
//冒泡排序法
inline void Score::paixu() {
	string Name1[100] = {};
	for (int k = 0; k < 3; k++) {
		for (int m = 0; m < times; m++) {
			Name1[m] = Name[m];
		}
		for (int i = 0; i < times; i++) {
			for (int j = 0; j < times - 1 - i; j++) {
				if (Grade[j][k] < Grade[j + 1][k]) {
					swap(Name1[j], Name1[j + 1]);
					swap(Grade[j][k], Grade[j + 1][k]);
				}
			}
		}

		if (k == 0) {
			cout << "科目名称:A" << endl;
			int i = 0;
			for (i ; i < times; i++) {
				cout << "姓名:" << Name1[i] << " ;成绩:" << Grade[i][k] << endl;
			}
		}
		if (k == 1) {
			cout << "科目名称:B" << endl;
			int i = 0;
			for ( i ; i < times; i++) {
				cout << "姓名:" << Name1[i] << " ;成绩:" << Grade[i][k] << endl;
			}
		}
		if (k == 2) {
			cout << "科目名称:C" << endl;
			int i = 0;
			for ( i ; i < times; i++) {
				cout << "姓名:" << Name1[i] << " ;成绩:" << Grade[i][k] << endl;
			}
		}
		
	}
}

问题1

debug过程:

for (int i = 0; i < times; i++) {
	    for (int j = 0; i < times - 1 - i; j++) {
				if (Grade[j][k] < Grade[j + 1][k]) {
					temp2 = Grade[j][0];
					Grade[j][k] = Grade[j + 1][k];
					Grade[j + 1][k] = temp2;
					temp1 = Name[j];
					Name[j] = Name[j + 1];
					Name[j + 1] = temp1;
				}
			}
		}

在初次执行的时候,无论怎么调试输出部分,都无法输出排序部分,只输出if语句中对应的cout内容。

为了排除交换出错改用swap函数进行交换,但是问题依旧存在。

后经单步执行调试发现内部for循环循环了20几次还没有跳出,发现把判断条件打错了,应为j < times - 1 - i。

void swap(float* a, float* b) {
	float tmp = *a;
	*a = *b;
	*b = tmp;
}
void swap(string* a, string* b) {
	string tmp = *a;
	*a = *b;
	*b = tmp;
}

通过定义了swap函数来执行数组内容的交换,

函数重载

在C++中,用户可以重载函数。这意味着,在同一作用域内,只要函数参数的类型不同,或者参数的个数不同,或者二者兼而有之,两个或者两个以上的函数可以使用相同的函数名。在使用的时候要注意以下几点:

  • 调用重载函数时,函数返回值类型不在参数匹配检查之列。因此,若两个函数的参数个数和类型都相同,而只有返回值类型不同,则不允许重载。
  • 函数的重载与带默认值的函数一起使用时,有可能引起二义性。
  • 在调用函数时,如果给出的实参和形参类型不相符,C++的编译器会自动地做类型转换工作。如果转换成功,则程序继续执行,在这种情况下,有可能产生不可识别的错误。

问题2

inline void Score::paixu() {
		for (int i = 0; i < times; i++) {
			for (int j = 0; j < times - 1 - i; j++) {
				if (Grade[j][k] < Grade[j + 1][k]) {
					swap(Name[j], Name[j + 1]);
					swap(Grade[j][k], Grade[j + 1][k]);
				}
			}
		}

		if (k == 0) {
			cout << "科目名称:A" << endl;
			int i = 0;
			for (i ; i < times; i++) {
				cout << "姓名:" << Name[i] << " ;成绩:" << Grade[i][k] << endl;
			}
		}
		if (k == 1) {
			cout << "科目名称:B" << endl;
			int i = 0;
			for ( i ; i < times; i++) {
				cout << "姓名:" << Name[i] << " ;成绩:" << Grade[i][k] << endl;
			}
		}
		if (k == 2) {
			cout << "科目名称:C" << endl;
			int i = 0;
			for ( i ; i < times; i++) {
				cout << "姓名:" << Name[i] << " ;成绩:" << Grade[i][k] << endl;
			}
		}
		
	}
}

上方代码是原来思路,原本在排序过程中,会出现成绩排序正确,但是后面科目B、科目C姓名和成绩发生错乱,对应不上,后面设定了一系列的值,来判断问题所在,排除了交换问题之后,发现是在科目A排序的同时,也打乱了Name数组本来的顺序,致使后面的排序是根据已经安照前面科目成绩大小排序过的Name数组来排序,会出现逻辑错误。

解决方案

在排序函数中定义一个大小和Name相同的数组Name1;每次排序之前,将Name的内容循环赋值给Name1,排序过程中对Name1操作,保证Name数组的顺序不变。

问题3

string Name1[100] = {0};

在定义Name1数组时,之前定义如上,出现了闪退报错,发现是格式书写有问题。

string Name1[100] = {""};

string Name1[100] = {};

上面这两种书写方式均可以运行。string 数组不能用0初始化。{0}方法初始化数组可能出现移植性问题,即并不是所有编译器都能通吃。

int main(){
	int m;
	Score x;
	cout << "默认学生数目为2" << endl;
	x.InputGrade();
	x.ShowGrade();
	x.ShowAvgGrade();
	x.paixu();
	cout << "请输入学生人数:(不大于100)" << endl;
	cin >> m;
	Score y(m);
	y.InputGrade();
	y.ShowGrade();
	y.ShowAvgGrade();
	y.paixu();
    system("PAUSE");//暂停,防止打包程序输出框执行完毕直接退出;
	return 0;
}

对象的定义和使用

通常把具有共同属性和行为的事物所构成的集合称为类。

类的对象可以看成该类类型的一个实例,定义一个对象和定义一个一般变量相似。

对象的定义:

  • 在声明类的同时,直接定义对象,即在类后直接定义。
  • 声明了类之后,在使用时再定义对象,如此处定义。

对象中成员的访问

对象名.数据成员名对象名.成员函数名[(参数表)]

访问属性

在Score类的定义中,将Grade定义为私有属性,私有成员只能被类中的成员函数访问,不能在类的外部,通过类的对象进行访问。可以通过类内函数进行访问。

一般来说,公有成员是类的对外接口,而私有成员是类的内部数据和内部实现,不希望外界访问。将类的成员划分为不同的访问级别有两个好处:一是信息隐蔽,即实现封装,将类的内部数据与内部实现和外部接口分开,这样使该类的外部程序不需要了解类的详细实现;二是数据保护,即将类的重要信息保护起来,以免其他程序进行不恰当的修改。

问题4

同学在运行代码时,出现了堆栈溢出的情况,一一检查对应代码,发现是Grade数组定义为了Grade[100][100],定义了一大堆用不到的空间,占了太多的堆栈。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值