第八周开始学习使用STL,并做了一个图书管理系统。做图书管理系统的过程中遇到了很多问题,代码也修改了很多遍。在修改的过程中,虽然花了很多时间,但也从中吸取了很多经验。
第一次做图书管理系统时,从早上10点开始,第二天下午才完成。写代码时也确实也遇到了不少麻烦。
在听完老师的讲解之后,我意识到我写的程序存在很多问题。在第一次作业中,我对STL还不是很了解,没有充分利用STL,写代码时只考虑到功能能不能实现,没考虑效率和其他方面的问题。于是我进行了第一次修改。
- 写日期类重载+运算符时,日期计算用效率低的for循环语句,听了老师的讲解后,就自己写了另一个效率比之前高的算法,这里我把数组a当作局部变量用。
CDate operator+(int d) {
int a[13] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 };
if ((year % 4 == 0 && year % 100 != 0) || (year % 4 == 0 && year % 400 == 0))
a[2] = 29;
day += d;
while (day > a[month]){
day -= a[month];
month++;
if (month > 12) {
month -= 12;
year++;
if ((year % 4 == 0 && year % 100 != 0) || (year % 4 == 0 && year % 400 == 0)) //更新a[2]的值
a[2] = 29;
else
a[2] = 28;
}
}
return *this;
}
- 还有日期类的<<运算符重载,当时为如何对yyyy/mm/dd格式的日期进行处理,后来在网上找到了方法,输入的时候用字符类型(char)变量放在year和month中间,以及month和day中间,字符类型变量读取’/'字符,year、month、day就能读取到yyyy、mm、dd。
friend istream& operator >>(istream& in, CDate& d) {
int a[13] = { 0,31,0,31,30,31,30,31,31,30,31,30,31 };
char b;
while (1) {
in >> d.year >> b >> d.month >> b >> d.day;
if (d.year >= 2020 && d.year <= 2021) {
bool flag = (d.year % 4 == 0 && d.year % 100 != 0) || (d.year % 4 == 0 && d.year % 400 == 0);
if (d.month == 2 && d.day <= 29 && flag)
return in;
else if (d.month == 2 && d.day <= 28 && !flag)
return in;
else if (d.day <= a[d.month])
return in;
}
}
}
- 在之前做增删查改时,是直接遍历vector找到符合条件的值,老师说这样做增删查改操作效率不高,在老师讲解之后用multimap建立索引。multimap里的元素是按key排序的,根据key查找时速度快。将要增删查改的元素放入key中,对应的vector下标放入value中,查找时以value的值确定被查找对象在vector中的位置,优化了增删查改的效率。
//修改前
void findBook(string ISBN) {
int length = book.size();
for (int i = 0; i < length; i++)
if (book[i].getISBN() == ISBN) {
book[i].showData();
break;
}
}
void deleteBook(string ISBN) {
int length = book.size();
vector<Book>::iterator iter = book.begin();
for (int i = 0; i < length; i++, iter++)
if (book[i].getISBN() == ISBN) {
book.erase(iter);
break;
}
}
//修改后
multimap<string, int>::iterator findBookByISBN(string ISBN, bool flag) {
multimap<string, int>::iterator it = bookISBN.find(ISBN);
if (it != bookISBN.end() && flag)
cout << book[it->second] << endl;
return it;
}
void deleteBook(string ISBN) {
multimap<string, int>::iterator it = findBookByISBN(ISBN, false);
vector<Book>::iterator iter = book.begin();
if (it != bookISBN.end()) {
book.erase(iter + it->second);
bookISBN.clear();
bookName.clear();
int size = book.size();
for (int i = 0; i < size; i++) { //重建multimap
bookISBN.insert(make_pair(book[i].getISBN(), i));
bookName.insert(make_pair(book[i].getName(), i));
}
}
}
- 在之前做文件读写时,是通过调用getX()方法进行读写,但这样写代码太繁琐。在第一次修改中,我通过重载<<和>>运算符来进行输入输出的文件读写功能,大大简化了代码长度。
//修改前
void printBook(string file) {
ofstream out(file.c_str());
int length = book.size();
for (int i = 0; i < length; i++) //代码又臭又长又繁琐
out << book[i].getISBN() << " " << book[i].getName() << " " << book[i].getEditor()
<< " " << book[i].getPublish() << " " << book[i].getDate()->getDate() << " "
<< book[i].getType() << " " << book[i].getTotal() << " " << book[i].getStore() << endl;
out << -1 << endl;
out.close();
//修改后
void printBook(string file) {
ofstream out(file.c_str());
int length = book.size();
for (int i = 0; i < length; i++) {
out << i + 1 << endl;
out << book[i] << endl; //代码简化
for (size_t j = 0; j < book[i].getRecordSize(); j++) {
Record r = book[i].getRecord(j);
out << r << endl; //代码简化
}
out << -1 << endl;
}
out << -1 << endl;
out.close();
}
- 第一次写这份作业时,我在操作类中创建了写文件的方法,但我没考虑到把写文件的方法加入到析构函数中,而是在main函数中调用。后来觉得在析构函数中调用更好些就做了修改。在写代码的过程中,为了节省时间,会复制代码中功能类似的部分,然后修改要替换的变量。但有两次复制后忘记修改,导致后面花了很多时间去查找出错的地方,这也算一次教训。对于借书、续借、还书的操作上,我第一次作业时把这三个功能写在了一个方法里,后来考虑到一个方法应该只实现一个功能,我就写成三个方法。
第二次修改我加入了模糊查询
- 模糊查询:用find_if()进行查询。在写谓词函数时,不知道如何进行模糊查询,在网上查找资料后,发现strstr()函数可以实现模糊查询。该函数搜索一个字符串在另一个字符串里的第一次出现,并返回所搜索到字符串在另一个字符串中的位置,如果没有搜索到,则返回0(NULL)。
另外,组合查询和区间查询的功能之后再添加。
总之,这份作业让我花了很多时间去修改。通过这次作业,我对这部分的知识有了更深的了解,也巩固了过去的知识。在这过程中,也因为自己的失误导致程序出错,浪费了不必要的时间去排除问题,下次要避免类似情况再次发生。