C++职工管理系统总结

提示:本文记录了学习完C++之后使用一个“职工信息管理系统”训练的感想和总结。


一、项目的总体架构

本项目是对职工的信息进行记录,并通过文件的读写操作将数据保存在文本文件中,方便以后查看。规划了一个Manage类,用来对信息的输入、显示、删除、查找等功能进行实现,而具体的员工方面,由于员工只是所在岗位有区别,其他的信息如年龄、姓名等都一样,所以可以使用继承和多态的思想,创建一个Workman类,作为员工类的抽象类(基类),而具体的员工如:大老板、小老板、普通员工等可以继承基类实现。

在Manage类中,建立一个Workman类型的二级指针,用来作为数组存放公司的每一个员工。在这里,之所以使用二级指针,是因为数组中只能存放类型一致的元素,但是职员类型分为三种:大老板、小老板、普通员工,所以必须用他们的指针进行存放。而他们的指针都是继承的基类Workman,因此可以存放到一个二维数组中。在系统中添加新员工时,可以根据待添加的员工类型,在堆区new出一个同样类型的数据,并保存到上述的二维数组中。

二、关键代码

1.创建空间及内存拷贝

代码如下:

	//更新公司中总的人数
	int old_num = this->workers_num;
	int new_num = old_num + in_num;
	this->workers_num = new_num;

	//为新成员开辟新空间
	if (this->Workers_arr != NULL)
	{
		Workman** tmp_workers = new Workman * [new_num];  //重新分配空间
		//注意:memcpy函数中第三个参数必须为 sizeof(Workman*) 而不可以是sizeof(Workman),否则会带来错误!
		memcpy(tmp_workers, this->Workers_arr, (old_num) * sizeof(Workman*) );  //复制原数组数据
		//for (int i = 0; i < old_num; i++)
		//{
		//	tmp_workers[i] = this->Workers_arr[i];  //本段循环用来替代memcpy函数
		//}
		delete[] this->Workers_arr;  //释放原有空间
		this->Workers_arr = tmp_workers;  //重新更新指针指向
	}
	else
	{
		this->Workers_arr = new Workman * [new_num];  //空数组直接创建空间
	}

2.析构函数实现内存的回收

代码如下:

Manage::~Manage()
{
	cout << "Manage析构函数调用!" << endl;
	//if (this->Workers_arr != NULL)  //这种方式仅仅将堆区的数组指针释放了,没有将用户创建的每一个堆区对象释放!是不合理的!
	//{
	//	delete[] this->Workers_arr;
	//	this->Workers_arr = NULL;
	//}

	for (int i = 0; i < this->workers_num; i++)  //堆区内存释放
	{
		if (this->Workers_arr[i] != NULL)//如果数据不为空
		{
			delete this->Workers_arr[i];  //先释放每一个堆区的对象
			this->Workers_arr[i] = NULL;
		}
	}
	delete[] this->Workers_arr;  //再释放堆区数组
	this->Workers_arr = NULL;
}

注意在析构函数中实现内存回收时,一定要注意深浅拷贝的问题!!。


三、调试过程中的相关问题

1.编译器报错:访问权限冲突:this指针是0xCCCCCCCC或是:0xCDCDCDCD

这种错误在百度上可以查到很多条相关信息,具体的原因就是指针的内存出错,要么就是使用了没有进行初始化的指针,或者是结构体等,必须要仔细查看程序中是否有指针创建出来后,程序员没有new出一片空间给这个指针分配内存!

我在debug时,出现了好多次这一问题:
原因就在于我直接加载了下面的Get_info()函数,而此时this->Workers_arr只是被定义了,而没有手动为他开辟内存空间,从而带来上述报错!

//重新加载文本中已有的数据
void Manage::Get_info()
{
	string tmp;
	int line_num = 1;  //行数
	int count = 0;
	string id;
	string name;
	string age;
	string sex;
	string department;  //1 大老板  2 小老板 3 员工

	Workman* tmp_worker = NULL;  //临时变量在程序结束后系统自动释放,不用手动delete

	ifstream ifs;
	ifs.open(FILENAME, ios::in);

	while (ifs >> name && ifs >> id && ifs >> sex && ifs >> age && ifs >> department)
	{
		if (department == "大老板")
			tmp_worker = new BigBoss(id, name, String_to_short(age.c_str()), (sex == "男" ? 0 : 1), 1);
		else if (department == "小老板")
			tmp_worker = new SmallBoss(id, name, String_to_short(age.c_str()), (sex == "男" ? 0 : 1), 2);
		else
			tmp_worker = new Employee(id, name, String_to_short(age.c_str()), (sex == "男" ? 0 : 1), 3);

		this->Workers_arr[count++] = tmp_worker;

	}
	ifs.close();
}

正确的方法:必须先为this->Workers_arr开辟空间,再调用Get_info()函数:

	//为原有数据分配空间
	this->Workers_arr = new Workman * [num];
	//将文本中的数据加载到系统内存中
	this->Get_info();

2.使用函数memcpy进行内存复制带来的运行错误

函数memcpy是C++中在 string.h 中(C++是 cstring)声明的函数。
函数原型
void *memcpy(void *destin, void *source, unsigned n);
作用
以source指向的地址为起点,将连续的n个字节数据,复制到以destin指向的地址为起点的内存中。
函数有三个参数,第一个是目标地址,第二个是源地址,第三个是数据长度。
注意事项:

  • 数据长度(第三个参数)的单位是字节(1byte = 8bit)。
  • 该函数有一个返回值,类型是void*,是一个指向destin的指针。
  • 使用这一函数务必确保第三个参数(待复制第二个数据)的长度,否则会带来内存泄漏!

这里我原来使用的代码:

memcpy(tmp_workers, this->Workers_arr, (old_num) * sizeof(Workman) );  //复制原数组数据

运行时总会出现意外的错误,因为有时候添加人员时没有问题,但是有时候会出现意想不到,摸不到头脑的错误,排查了好久后发现:
tmp_workers是一个Workman**类型的指针,在拷贝数据的时候,必须拷贝类型相同的字节长度,因此修改为:

memcpy(tmp_workers, this->Workers_arr, (old_num) * sizeof(Workman**) );

之后,问题就得到了解决!

使用memcpy函数时,特别要注意数据长度。
如果复制的数据类型是char,那么数据长度就等于元素的个数。而如果数据类型是其他(如int, double, 自定义结构体等),就要特别注意数据长度的值。
好的习惯是,无论拷贝何种数据类型,都用 n * sizeof(type_name)的写法。

四、C++项目中文件管理经验

  1. 对于自定义的头文件要使用双引号包含;而对于系统给定的头文件要使用<>进行包含
  2. 先创建一个main.cpp文件,作为程序的核心代码(可以更换为其他的名字,中文也可!)
  3. 对于每一个类,创建一个.cpp和.h文件,类的声明(及内部的成员函数和成员变量)都放在.h文件中,而类的具体函数实现需要在.cpp文件中进行。
  4. **需要注意的是,**由于类的声明和实现不在同一个文件中进行,因此在对自己写的源文件(.cpp中)成员函数进行具体实现时,必须加上作用域:即类型::。
  5. 在自己写的类中,对自己写的源文件(.cpp中)成员函数进行具体实现时,如果需要调用到该类的其他成员变量及成员函数,必须使用this指针指向这一变量或函数,如:this->name等。
  6. **注意:**在每一个自己写的.h下,都要包含iostream.h和using namespace std;

五、总结

代码的经验必须多写才能总结,之前我一直比较懒,以为看看书就可以了,现在才发现必须要多动手实践才可以将知识转换为实际!加油吧!

具体的工程文件代码详见我的博客C++专栏。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值