学习总结2

知识点

STL

1、STL是c++封装的一组容器。因为使用的泛型进行的封装,所以数据可以是任意类型的。并且只能存放值或者指针,不能存引用
2、STL主要有但部分组成:容器,迭代器,算法
3、容器有vector(向量,又称为动态数组) 、map(存储的是键值对,当需要进行大量的查找操作时,使用map)、set
4、迭代器是严格区分类型的,使用时要明确是哪一个类型的容器的迭代器。
例如:图书类型的向量的迭代器:vector :: iterator 迭代器变量名;

5、在这里插入图片描述
6、一些常用的通用操作
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
7、vector

  • 初始化方法:
    vector<t> v; //初始化成一个新向量
    vector<t> v(a,a+size)//使用数组初始化向量
  • push_back():在末尾添加一个元素
  • pop_back():删除最后一个元素
  • 迭代器遍历:vector <t> v;vector <t> :: iterator::i;for(i=v.begin();i<v.end();i++){}

8、map

  • map的迭代器map <t> m;map<t>::iterator i;
  • 查找函数 find():如果有这个元素,返回他的迭代器指针;如果没有,返回m.end()
  • 对于其中的每一个元素,都分为key,value。获取key,使用 i->first,获取value,使用i->second
  • 添加方法:insert(key,value)
  • 删除在这里插入图片描述

9、算法(针对所有容器)

在这里插入图片描述在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
说明:
1、以上算法在的first,last是一个查找区间相当于[first,last)。是迭代器指针
2、count_if():定义在区间上的查找条件,通常是一个函数。
3、使用到<,>的,如果是对象,要重载运算符
查找:一下查找方法适用于任何容器

  • 精确查找
  • 模糊查找:find_if()使用时如果是类,需要==重载运算符
  • 查找返回的是迭代器指针
    在这里插入图片描述

在这里插入图片描述
排序
在这里插入图片描述

作业分析

作业要求

图书管理系统(后台管理员)

数据类:
基础类:读者最大借书量、最长借书时间(按天计算)。这两个都是类成员
日期类:包括年月日,重载输入运算符时,要进行数据的合法性检验;重载输出运算符时,按照“年/月/日”形式输出;重载+运算符;
借阅记录类:包括日期、书号、读者学号、类型(借出/还回/续借)、图书类型(基础/文学休闲/专业);
读者类:学号(常成员)、姓名、专业、班级、已借图书数量、借阅记录向量;
图书类:书号(常成员)、书名、作者、出版社、出版日期、图书类型(基础/文学休闲/专业)、馆藏总量、在馆数量、借阅记录向量;
操作类:
数据成员:图书/学生/借阅记录向量
成员函数:对图书进行文件读写、在图书向量内完成对图书基本信息的增删查改;
对学生进行文件读写、在学生向量内完成对学生基本信息的增删查改;
借阅记录的管理和统计功能后续添加;

对于每个类的分析

1、日期类

  • 日期类中数据成员和成员函数。
    数据成员首先想到年,月,日。
    成员函数分为构造函数和普通函数。
  • 对于构造函数,必须要有默认的无参构造函数和带三个参数的构造函数,最好写上赋值构造函数,便可以用已知的时间初始化一个时间。
  • 对于普通函数,首先要有的是各个参数的get,set函数,用于获取具体的年月日
  • 其次,要重载属于输入/出运算符。再次复习一下内容
    1、首先,使用友元函数。具体的函数结构:
    friend ostream & operator>> (ostream& in, Date &d) {in>>对象的参数;...return out;}
    friend istream & operator<< (istream& out,Date &d){out<<对象的参数;...return out;}
    2、其次,重载后的输入运算符,既可以用于键盘输入,也可以用于读文件。
    ⭐注意:读文件时,文件的内容必须符合形式。比如说,读取的日期的内容应该参数之间以空格隔开,并且是整数的形式。如果我在文件中存储的是2020/4/26,那么便无法使用<<读取
  • 然后,重载赋值运算符。因为后面的类中,少不了赋值。Date类是作为成员变量存在,所以必须重载赋值运算符。
  • 然后,重载加法运算符。
    1、需求来源:借书时,需要明确想要借书的最大时间,通常是天数,因此要计算最晚的还书日期。
    2、方法分析:
         有三种情况:
         1、还书日期还在本月,那么用借书的day+借书天数即可
         2、还书日期不在本月,但是在本年。那么先计算出本月的剩余天数。用借书天数-本月剩余天数,然后循环。直到某一个月的天数>此月所需要的天数。那么月份便是此月份,天数即使剩余的天数。
         3、对于2中的情况,如果出现加一个月后月份为13,那么月份变成1,年数+1,使用2中的方法继续计算。
        ⭐因为在此过程中需要判断剩余天数是否大于当月所有的天数,所以可以在成员变量中添加一个成员变量:天数数组。 int d[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};。
    3、写代码过程中出现的错误:
    a、判断闰年的函数马虎写成了y&100!=0
    b、如果函数返回值为Date,不能直接输出cout<<d+10<<endl;cout<<d+93<<endl;
    要写成Date d1=d+93;cout<<d1<<endl;
    但是如果是对象的引用,可以直接写cout<<d+10<<endl;cout<<d+93<<endl;
    4、代码:
bool isRunNian(int y)
{
    if (y % 400 == 0 || (y % 4 == 0 && y %100 != 0))
    {
        return true;
    }
    else
        return false;
}

       Date operator+(int day1)
    {
        if (isRunNian(year))
        {
            d[1] = 29;
        }
        else
        {
            d[1] = 28;
        }

        int temp;
        temp = this->day + day1;
        if (temp <= d[month - 1])
        {

            year = this->year;
            month = this->month;
            day = temp;
        }
        else
        {
            //11/20 65
            day1 = day1 - (d[month - 1] - day);
            month++;
            while (d[month - 1] < day1)
            {
                month++;
                if (month > 12)
                {
                    month = 1;
                    year++;
                }
                day1 = day1 - d[month - 1];
            }
            day = day1;
        }

        return *this;
    }

2、读者类和图书类

  • 读者类和图书类相似,因为他们的成员变量中不仅有变量,还都含有常量。
  • 对于常量的处理:
    1、在构造函数中,使用初始化列表赋值
    2、在赋值构造函数和重载赋值运算符和重载输入运算符中,使用const_cast<指针>(常量的地址)
    string *temp = const_cast<string *>(&xh); *temp = tu.xh;
    对于const_cast的说明:const_cast使得一个未用const修饰的指针int *temp可以指向const修饰的变量int xh。因此,这个可以对*temp进行赋值。比较特殊的是,对*temp进行赋值后,temp所指向的地址的确是xh变量所在的地址,*temp的值也的确是修改后的值,但是常量xh的值并没有改变。
    例如:
    const int a=10;
    const int*d=&a;
    int *f=const_cast<int *>(d) ;
    cout << a << endl;
    cout << *d << endl;
    cout << *f << endl;

    *f = 20;
    cout << a << endl;
    cout << *d << endl;
    cout << *f << endl;

    cout << &a << endl;
    cout << d << endl;
    cout << f << endl;

3、代码:

  duzhe &operator=(const duzhe &tu)
    {
        string *temp = const_cast<string *>(&xh);
        *temp = tu.xh;
        name = tu.name;
        zy = tu.zy;
        bj = tu.bj;
        count = tu.count;

        return *this;
    }
  • 然后成员函数就是get,set函数,重载运算符函数。

3、操作类

  • 操作类是最重要的类。
  • 在操作类中,最重要的是对类的理解。在全局上的理解,有两个文件,分别存储图书馆所有的图书信息和读者信息。通过这个系统,我们可以对图书和读者进行增删改查的工作。文件是存储在硬盘上的,而我们的增删改查是在内存上进行的,所以需要读取文件内容存放在内存,使用后为了长期保存将其存储在文件里。提到一个类,除了成员变量外,就是函数。构造函数的作用是初始化,这里的工作就是读取文件中的内容。析构函数的作用就是将修改后的内存中的信息放到文件中永久保存。。
  • 操作类的成员变量是 三个向量。
  • 第一次写的时候,没考虑到的问题:没有弄清楚读文件和写文件的作用。并且由于没考虑到图书馆中的读者和图书的数量肯定非常多,所以在查询时和查重时使用遍历的方法,效率低。为了提高查询效率,添加map数据成员。
    实现方法
    创建两个map类型的数据成员,一个用来存储读者的学号和读者在向量中的下标。一个用来储存书号和在向量中的下标。那么在查询时使用find()方法,可以快速找到。
  • 具体功能的实现
    增:增加时,需要看原来是否已经存在。对于读者,如果已经存在,则不用再添加。对于图书,如果已经存在,则在图书向量中不要再添加一个图书对象,而是将原来的图书的在馆数量和馆藏数量+1。
    改:通过map快速找到对象,更新数据。但是更新时不要修改学号和书号。如果没有,不用更新
    删:删除图书和读者,都是通过map找到对象所在的下标。如果存在,通过erase(迭代器)方法删除。
    注意:
    删除后,要重建map。因为向量中的每个对象的下标改变了。重建就是删除原来的,重新添加数据
    void deleteDz(string xh)
    {
        multimap<string, int>::iterator i;
        vector<duzhe>::iterator it = dz.begin();
        i = duzheXh.find(xh);
        if (i != duzheXh.end())
        {
            int x = i->second;
            dz.erase(it + x);
            //map重建
            duzheXh.clear();
            duzheName.clear();
            vector<duzhe>::iterator it1 = dz.begin();
            int a = 0;
            for (it1; it1 < dz.end(); it1++)
            {
                duzheName.insert(make_pair((*it1).getName(), a));
                duzheXh.insert(make_pair((*it1).getXh(), a));
                a++;
            }
        }
        else
            return;
    }

        查:按照学号、书号查和按照名字查。还是先在map中的value中查询是否有对应的内容。没有就算了,如果有的话,如果是结果只有一个,直接输出;如果结果有多个,需要使用lower_bound和upper_bound.
例如:

  void searchByTsName(string name)
    {
        multimap<string, int>::iterator i, a, b, c;
        i = tushuName.find(name);
        if (i == tushuSh.end())
        {
            return;
        }
        else
        {
            a = tushuName.lower_bound(name);
            b = tushuName.upper_bound(name);
            for (c = a; c != b; c++)
                cout << ts[c->second] << endl;
        }
    }

  • 困扰我许久的问题
    1、总是不能在控制台输入数据
    当我写完操作类进行测试时,当文件为空时,可以输入数据和写进文件。但是再次使用时便不可输入。后来我才知道,原来是我的文件的写入格式不符合读文件的格式。比如说日期写成’2020/4/27‘,但是读的时候应该是 ‘2020 4 20’
    2、每次读出来的内容,最后一条的数据的最后一个数据总是输出两遍
    在读文件过程中,使用eof()放法作为判断是否读完的标志。对于此方法,如果文件为空,不进行读操作。如果文件不为空,eof()方法确实存在这个问题。
    解决方法:直接使用读取过程作为判断标准while (ifs >> t) { dz.push_back(t); duzheName.insert(make_pair(t.getName(), dz.size() - 1)); duzheXh.insert(make_pair(t.getXh(), dz.size() - 1)); // cout<<ifs.eof()<<endl; }

3、每次使用迭代器输出图书信息,总是没有书名
       因为我后来写了一个复制构造函数,但是我写错了。但是我改正后答案还是不对。后来我删掉这个函数结果就正确了😔但是后来我添加了这个函数,结果又正确了,不知道当时咋回事。

  • 小问题
    1、引用的问题:方法里定义的局部对象不能返回引用
    2、数据结果中出现了随机数,可能是因为变量进行了重声明,或者某一个指针变量的含义理解错误

学习总结

通过最近的这次作业,我感觉做系统最重要的是理解,然后是学以致用。
掌握每个知识的的使用场景非常重要。最开始的时候,我就没有理解题意,导致内容做的不对,通过老师的讲解,自己在一些地方才理解什么意思。再有就是一些知识的的理解不够深刻。以后,我写的每一个程序,我要力求明白它存在是否有意义,没有意义的功能不写,写了的功能理解其中的每一部分的作用。
并且,这次作业我写的非常乱,因为我遇到错误了,想要按保存原来的,然后再修改,最后弄了很多文件,自己都乱套了。我记得我把那个注释的代码删掉了,难道是没保存?之后我还是要有条理一些。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值