Halo

A magic place for coding

0%

中山大学初级实训

前言

  中山大学软件学院的大一想必都会经历这样一个初级实训。这个实训主要是完成一个基于命令行的会议管理系统,使用的语言是C++,含有部分C11特性。主要考察的是大家C++的编程知识,难度不算很大,但是需要大家很细心。因为每天只有一次测评的机会,因此拿满分还是有点难度的。本人不是大佬,当时实训横跨了三个周末,debug找错的也很煎熬,但是最后还是做出来的,在这里也是想分享一些经验,希望能帮助到师弟师妹们。想要代码的直接点左上角去github看,或者直接点击这个,但是还是希望大家能够自己完成。

实训介绍

  实训的开发环境是云平台的Liunx,这要求大家对Linux要有一定的熟悉程度。从得分的角度,内容主要有以下几点:

  • 命令行操作(第一周TA检查)
  • makefile编写(第二周TA检查)
  • User、Date、Meeting三个类的实现(第一周)
  • Storage、AgendaService两个类的实现(第二周)
  • UI的实现、加分项(第三周)

  接下来我将分点介绍上面这几个内容,务求让大家多拿分。

命令行操作

  实训使用的环境是Linux系统,在里面编译、运行、调试C++
程序需要使用命令行。很多人一直使用的都是在Windows环境下的IDE如DevC++,一下子转到Linux下使用命令行会觉得很不习惯也很不方便,但当你熟练运用以后会发现,其实命令行也有它的过人之处。
  TA检查的时候主要检查大家的文件系统操作,如打开文件(夹)、创建文件(夹)、删除文件(夹)、复制文件(夹)、重命名。一般都不会刁难大家,临急抱佛脚也是可以的,但是如果对Linux比较熟悉的话,pass这个检查也是完全没问题的。这里简单举几个例子:

  • 创建文件夹。创建一个名为test1的文件夹
    1
    mkdir test1
  • 打开文件夹。进入刚刚创建好的test1文件夹
    1
    cd test1
  • 在test1文件夹中创建名为test.cpp的文件
    1
    touch test.cpp
  • 查看当前目录下所有文件,如果要查看隐藏文件,参数加上-a
    1
    2
    ls
    ls -a
  • 用Vim或SublimeText打开test.cpp文件
    1
    2
    vim test.cpp
    sub test.cpp
  • 删除test1.cpp文件
    1
    rm test1.cpp
  • 删除文件夹test1
    1
    rm -r test1

makefile编写

  Makefile是C++工程项目里面的一个很有用的工具。当我们需要编译一个工程时,往往会涉及许多文件,在命令行中逐个文件和参数输入效率是很低的,如果我们提前编写好makefile,每次需要编译运行的时候就输入make命令,编译器就会根据文件中制定好的顺序和路径逐个编译并链接文件,最后运行程序,这样就大大地提高了我们的开发效率。
  TA主要检查对makefile语法的理解和使用,然后会让你跑一个提前写好的makefile,难度基本也不大,提前熟悉一下语法就可以了。这里推荐一个博客的教程

数据层实现

  第一阶段是实现数据层的内容,包括了User、Date、Meeting三个类,主要是一些属性的get和set方法,这一部分难度是很小的,写代码的时候注意细节,写完后自己测试一下,基本上可以一次通过测评。
  这里有点难度的是Date里面对日期先后的大小比较和日期有效性的判断。日期先后的比较很巧妙地依赖于格式化后字符串的比较。有效性的判断第一是格式的有效性,第二是逻辑有效性。

  • 逻辑有效性
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
     bool Date::isValid(const Date &t_date)
    {
    if( t_date.m_year < 1000 || t_date.m_year > 9999 )
    {
    return false;
    }
    else
    {
    if( t_date.m_month <= 0 || t_date.m_month > 12 )
    {
    return false;
    }
    else
    {
    if( t_date.m_day <= 0 || t_date.m_day > daysOfMonthy( t_date.m_year, t_date.m_month ) )
    {
    return false;
    }
    else
    {
    if( t_date.m_hour < 0 || t_date.m_hour > 23 )
    {
    return false;
    }
    else
    {
    if( t_date.m_minute < 0 || t_date.m_minute > 59 )
    {
    return false;
    }
    }
    }
    }
    }
    return true;
    }
  • 格式有效性
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    int flag = 0;
    for( int i = 0; i < t_dateString.size(); i++ )
    {
    if( i == 4 || i == 7 || i == 10 || i == 13 )
    {
    continue;
    }
    else
    {
    if( t_dateString[ i ] < '0' || t_dateString[ i ] > '9' )
    {
    flag = 1;
    break;
    }
    }
    }
    if(t_dateString[4] != '-' || t_dateString[7] != '-' || t_dateString[10] !='/' || t_dateString[13] !=':' )
    {
    flag = 1;
    }
    if( t_dateString.size() != 16 )
    {
    flag = 1;
    }
      完成上述两个关键部分,基本上这个一阶段就没什么难点了。

    逻辑层实现

      第二阶段是逻辑层的实现,这里牵涉到了一些数据的逻辑问题,比如说组合多各类的问题等等。同时,这一块的难度也是最大的,因为牵涉到了大量的遍历,因此数组访问越界的问题常常会导致内存泄漏(本人就是被越界问题坑了两个星期),这里需要用到gbd调试工具来帮助我们找到内存泄漏的地方。
      这里着重提及一个很容易越界的地方。在遍历数组时,如果对元素有涉及到删除操作,就很容易导致越界。这里以deleteUser()为例。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    int Storage::deleteUser(std::function<bool(const User &)> filter)
    {
    int delete_num = 0;
    for( auto it = this->m_userList.begin(); it != this->m_userList.end(); )
    {
    if( filter(*it) )
    {
    it = this->m_userList.erase(it);

    delete_num++;
    }
    else
    {
    it++;
    }
    }
    m_dirty = true;

    return delete_num;
    }
      需要特别注意在调用erase()后,迭代器指针it实质上已经指向了下一个元素,如果这时候还是直接it++,就很有可能出现越界,因此需要把以前循环遍历的格式改为以上这种添加了if和else的方法,来避免越界的产生。

UI实现与创新加分

  第三阶段的内容是UI实现与创新部分。因为许多同学在第二周的时间内是完不成第二阶段的内容,因此第三周的部分实现也要用来填第二周的坑,UI部分和创新内容的时间会相对较少。
  UI方面TA会提供一个标准的样式,大家只需要对着写就可以了,可以说没什么难度,就是一些格式上的东西需要注意,整体做出来没什么大差错就可以了。创新部分需要自己头脑风暴,比较多同学做的是UI的优化(比如删除前添加一个确认的提示)、输入密码加密(命令行界面中显示*)、密码加密等。基本上TA都会给大家满分的,所以只要自己用心做了,基本上都会是满分。

总结

  我这一届的初级实训是在大一下学期中进行的,横跨了三个周末,相比起以往在小学期进行实训,感觉时间更加紧迫,也要同时兼顾其他科目的作业。过程中自己也是处于一个不断摸索的阶段,对于一些C11的新特性也要自己学习。
  最困难的部分还是Storage的实现,基本上实现了这个类就完成了实训的85%。这个类是逻辑层的核心部分,连接着UI和数据,本人一直在这里卡死,连续四次测评的结果都是RE(Runtime Error)。最后对每一个遍历都进行了仔细的测试,真的是逐行代码检测,自己也写了上百个测例,终于在最后一次测评的时候得到了满分。
  整个阶段最重要的还是心态,初级实训涉及的都是一些很基础的东西,并没有算法方面的内容,因此大家还是要沉住气,仔细一点,就算是测评不过也要继续找bug,我相信大家都一定可以在实训中取得很好的成绩的!

Welcome to my other publishing channels