[2019BUAA软工助教]第0次代码作业

[2019BUAA软工助教]第0次代码作业

前言

本博客为完成结对项目所需的先导知识,题目不难,请认真对待 😃

欢迎来到软件工程 😃

注:本次实验为附加作业,不做不扣分,做了有附加分

Part 0. 背景

阿超家里的孩子上小学一年级了,这个暑假老师给家长们布置了一个作业:家长每天要给孩子出一些合理的,但要有些难度的四则运算题目,并且家长要对孩子的作业打分记录。

作为程序员的阿超心想,既然每天都需要出题,那何不做一个可以自动生成小学四则运算题目与解决题目的命令行 “软件”呢。他把老师的话翻译一下,就形成了这个软件的需求:

  • 程序接收一个命令行参数 n,然后随机产生 n 道加减乘除(分别使用符号+-*/来表示)练习题,每个数字在 0100 之间,运算符在 2 个 到 3 个之间。

  • 由于阿超的孩子才上一年级,并不知道分数。所以软件所出的练习题在运算过程中不得出现非整数,比如不能出现 3÷5+2=2.6 这样的算式。

  • 练习题生成好后,将生成的 n 道练习题及其对应的正确答案输出到一个文件 subject.txt 中。

  • 当程序接收的参数为4时,以下为一个输出文件示例。

    13+17-1=29
    11*15-5=160
    3+10+4-16=1
    15÷5+3-2=4
    

这次阿超选择使用他最拿手的 C++ 语言来完成这样的需求,工欲善其事必先利其器,第一步就需要先安装一个好用的 IDE ,在这里我们推荐使用 Visual Studio 2017

Part 1. 配置环境

Visual Studio 2017 有着宇宙最强 IDE 的美称,它对 C++ 的支持也很好,在本教程中,为简化学员的开发难度,我们选择使用 VS2017 社区版(社区版(Community)指的是可免费提供给单个开发人员,给予初学者及大部分程序员支持,可以无任何经济负担、合法地使用的版本。)

下面提供三种安装方法:

  1. 官网下载:点击下载链接,浏览器将自动开始下载

  2. 下载迅雷,输入以下ed2k链接:

    ed2k://|file|mu_visual_studio_community_2017_version_15.3_x86_x64_11100062.exe|1069960|5984B3CD547F9F213DE21EFE5887F08D|/
    
  3. 百度网盘:链接: https://pan.baidu.com/s/1jJXyRMA 密码: ub6c

下载的文件只是一个引导安装程序,下载完成后,双击运行,如果出现下面的 Visual Studio 提示:

Visual Studio 需要 .NET Framework 4.6 或更高版本

则需要首先自行安装版本较高的 .Net Framework( .NET Framework 4.6 可以在这里下载到)如果没有出现该提示,请忽略。

在点击开 exe 文件后,一路继续,可以看到如下的选择界面。由于我们只需要 C++ 库,所以只需要勾选 【使用 C++ 的桌面开发】即可。由于 VS 本身体积较大,推荐修改存储位置,将其安装在非系统盘目录。

选择安装组件

如果顺利的话,过一大段时间 VS2017 就会下载好相应文件,此时会提示要求重启,此时按照指示重启即可。

安装成功后,首次使用 VS 2017 还需要对其进行简单的配置,包括开发环境的主题风格。如果要求登录的话,可以使用你的outlook账号登录,或者可以选择忽略。

这里开发设置选择 C++即可,主题推荐深色主题。

Part 2. 克隆项目

阿超的项目放在了当下最流行的源代码管理平台 Github 上,仓库地址。那么,我们如何在阿超项目的基础上进行开发呢?

  1. https://github.com/join 这个网址处申请注册一个 Github 账号,申请成功后可在 https://github.com/login 处利用刚刚注册的账号进行登录,才能开始在 Github 上进行开发。

  2. 成功登录后,输入阿超仓库的网址 https://github.com/ChildishChange/Calculator ,点击右上角的 Fork ,将阿超的四则运算库拷贝到自己的同名仓库中,如下图所示:

    fork repo

  3. 拷贝成功后,可以看到自己已经拥有了一个同名仓库。这里我们登录的是 buaase 的账号:

    my repo

  4. 在自己的电脑上安装 Git 软件,Git 的安装教程在这里。在自己拷贝项目的主页的绿色按钮处可以找到一个可克隆的项目地址,下面是一个示例:

    clone repo

  5. 我的电脑 中任意找一个目录,打开 Git 命令行软件(Windows上可在空白处右键打开 Git Bash ),输入 git clone <clone url> ,其中 <clone url> 即我们刚刚复制的项目地址。一个动W态演示图如下所示(这里 https://github.com/buaase/Calculator.git 就是我们 Fork 后仓库的地址):

    git clone repo

  6. 在完成上述操作后,可在当前目录下看到一个与仓库同名的文件夹Calculator ,这就是克隆到本地的项目。注意,默认克隆的分支是 java,请使用 git checkout cplusplus 命令切换到 C++ 项目。 进入项目文件夹,新建一个文件夹,重命名为你的 Github 账号名。

    git checkout c++

  7. 以 Github 账号命名的文件夹作为项目目录,打开 VS2017,点击左上角的 文件 -> 新建 -> 项目 ,如下图所示,选择 Visual C++ 中的 控制台应用程序 。注意更改 位置 参数到刚刚新建的文件夹所在的路径。比如我的账户名为 buaase ,刚刚新建的文件夹路径为D:\Calculator\buaase ,解决方案的名称也可以是 Calculator ,自定义即可。
    initialize project

  8. 新建项目后会出现一些默认的 stdafx.cpp.h 为后缀的头文件。找到与解决方案同名的 cpp 文件,比如解决方案为 ConsoleApplication1 ,那么会有一个名叫 ConsoleApplication1.cpp 的文件。将 src 目录下 Calculator.cpp 文件的内容拷贝到该文件中。右键点击 头文件 ,新建一个头文件,修改名称为 Calculator.h ,并将 src 目录下 Calculator.h 文件的内容拷贝到新的头文件中。此时,右键点击解决方案,选择 编译解决方案,成功后点击 本地Windows调试器 即可运行,示意图如下:

    choose local

  9. 接下来接连使用 git add,git commit -m "Message"(Message是你要写的内容)即可利用 Git 记录下所有的改动。如果是初次使用 Git,请在使用上述两条命令前使用如下两条命令配置自己的个人邮箱与 Commit 时的用户名,这里的邮箱和用户名最好与 Github 账号保持一致。

    $ git config --global user.name "John Doe"
    $ git config --global user.email johndoe@example.com
    

    下面是一些常见的Git操作,可留作备忘

    $ git clone [url]
    下载一个项目以及它所有的版本历史
    
    $ git add [file]
    将文件进行快照处理用于版本控制
    
    $ git commit -m"[descriptive message]"
    将文件快照永久地记录在版本历史中
    
    $ git push
    上传当前本地分支commit到GitHub上
    
    $ git pull
    下载服务器上最新的本部并合并更改到本地
    
    $ git reset [commit hash]
    撤销所有[commit hash]后的的commit,在本地保存更改
    
    $ git log
    列出当前分支的版本历史
    

    对于Github平台有疑问的,可以在 http://github.com/help 找到解决方案。

Part 3. 单元测试

要想在 VS2017 里对 C++ 项目进行单元测试,首先要新建一个测试项目。右键单击解决方案,可以添加一个新建项目,在类型里选择 单元测试,我们这里新建了一个名为 CalculatorUnitTest 的单元测试项目。

create a unit test

add this unit test

在项目创建成功后,为单元测试项目 CalculatorUnitTest 增加对原项目的引用,以实现调用原项目函数接口的功能。

add reference

光设置引用还不够,接下来让我们手动设置一下测试项目的附加依赖项。选中单元测试项目,右键点击选择 属性

choose property

在打开的左侧菜单栏中选择 配置属性 -> 链接器 -> 输入 ,在 附加依赖项 的下拉选择框中选择 <编辑...>,将被测试项目产生的所有 obj 文件路径(注意:并非每次单元测试都固定写 Calculator.obj ,它决定于 C++源文件的名字)写到附加依赖项中,如下图所示:

edit property

这两个obj 文件可以在如下图文件夹(Project/Debug/)中找到:

obj files

在完成单元测试的项目配置后,下面我们就可以开始写单元测试代码了。首先看向新创建的单元测试项目,里面应该会有一个默认的 unittest1.cpp 文件,打开该文件,应该是长这样的:

#include "stdafx.h"
#include "CppUnitTest.h"
using namespace Microsoft::VisualStudio::CppUnitTestFramework;

namespace CalculatorUnitTest
{		
	TEST_CLASS(UnitTest1)
	{
	public:
		
		TEST_METHOD(TestMethod1)
		{
            //请输入测试代码
		}
	};
}

为了能顺利引用刚刚写好的接口,我们需要引入Calculator工程的接口定义头文件,即 calculator.h。在头文件部分插入一行

#include "../Calculator/calculator.h"

然后我们就可以顺利引用相关接口了。通过阅读方法 Solve 的注释与代码,我们不难发现,它的参数是字符串形式的四则运算表达式,返回的应该是该表达式的答案。现在我们来填充第一个测试用例,TEST_METHOD 其实就是一个宏,后面跟着的 TestMethod1 才是单元测试真正的名字。在该方法中插入如下代码块:

Calculator* calc = new Calculator();
string ret = calc->Solve("11+22");
Assert::AreEqual(ret, (string)"11+22=33");

这里我们用到了 Assert(断言)。编写单元测试时,我们总是会做出一些假设,比如我们期望一个函数在接受预期的输入后就返回预期的输出,断言就是用于在代码中捕捉这些假设。一般来说,单元测试中都会有断言的存在,没有断言存在的单元测试其实是“假大空”的,没有任何对程序输入输出的假设约束。

下面我们来运行一下这个单元测试,看 Solve 函数是否符合我们的期望。找到菜单栏中的 测试 ,运行所有测试即可,如下图所示

run all tests

在单元测试运行完毕后,VS 的左侧会弹出一个测试结果窗口。绿色代表通过,红色代表失败。从本次的结果来看,我们通过了这个单元测试。

unit test passed

那么也就是说,当 solve 函数的输入为 "11+22" 时,其实际的输出就是 "11+22=33",与预期现象温和。当然,我们这里只是通过了一个简单的测试用例,不能说这个函数就是一定正确的。所以我们需要加一些单元测试,以验证在怎样的情况下这个函数可能会出错,现在请你帮助阿超补充一些针对 solve 方法的单元测试用例吧。

Part 4. 基本操作

上面我们学习了如何使用 IDE 进行单元测试,也测试出了 solve 方法的确有些问题。那么该如何定位问题所在呢?这就要用到 IDE 的调试功能了。下面我们就来介绍一下 Visual Studio 的调试方法。

  1. 断点

    调试程序首先要会设置断点和单步运行。在 VS 中设置断点非常简单,在要设置断点的行号旁用鼠标单击一下就行了(注意要点到与右侧编辑器颜色明显不同的区域),如下图所示,我们在第 31 行设了个断点:

    break point on line 31

  2. 单步运行

    在设置好断点后,我们就可以启动 Debug 模式。我们这里由于默认启动项目是 Calculator ,所以直接点击如下所示的 本地 Windows 调试器 按钮即可开始调试。

    choose local

    如果一个解决方案中有多个Cpp项目,要首先指定启动项目。

    set start project

    启动调试后,这时可以看到程序已经运行到刚刚打的断点处前。下方的自动窗口可以看到各个变量的值。

    check value

    与其他 IDE 类似,我们也可以通过手动设置监视一些感兴趣的变量。点击选项卡到 监视 1 中,按照下图所示方式添加监视,可在界面中只显示监控变量的值。

    choose monitor 1

    此时我们的第31行代码并没有执行,下面我们利用单步运行的方法执行该语句。单步运行有两种:Step Into(逐语句,快捷捷F11) 和 Step Over(逐过程,快捷捷F10),分别对应这两个图标。(这些图标都在刚才启动本地调试器按钮的旁边)

    step into/over

    这两种单步运行功能在运行语句时没有区别,在执行方法调用语句时,Step Into 会跳入方法实现,Step Over 会直接执行完方法,实际使用中我们优先使用 Step Over,只有方法执行出错了,说明程序问题在被调用方法中,这时再回来通过 Step Into 进入方法进行调试。我们单击一下 Step Over 图标(或 F10 ),程序停在了第 33 行(向右的小黄箭头指示目前执行到第几行,也可以使用这个图标代表的按钮 )

    show next line

    这时我们可以看到执行了 31 行代码后 formulaChar 目前的值。

  3. 条件断点

    有些情况下我们只希望断点在某些条件下才成立,该如何做呢?其实非常简单,右键单击红色的断点符号,即可弹出条件选项。

    add conditional break point

    在这里我们可以输入 Condition,设定为只有某些前置变量的值满足条件时我们才会触发断点,帮助我们高效率测试。比如我们这里设定 Condition 为 j == 0

    set condition

    从下图中我们可以看到当断点生效时,j 的值为 0。

    check condition

    我们现在的程序中暗藏了两个BUG,一个是不符合题目的需求,另一个是实现上有一个小问题。现在请你利用刚学习到的单元测试与 Debug 的相关知识,找出程序的 Bug 吧!

Part 5. 回归测试

单元测试不仅仅用来保证当前代码的正确性,更重要的是用来保证代码修复、改进或重构之后的正确性。也就是说,在每次修改完 Bug 之后,我们其实都需要运行一遍来看看是不是满足之前所有的单元测试样例。所以,在每次因为现有的 failed test 而修复原有代码后,最好都全部运行一遍单元测试,保证以前 passed test 仍然是可以通过的。

同样地,Git 的使用也是讲究勤提交,提交的粒度最好是细到每个小功能的完成。一个小功能可以是一处小 Bug 的修复,也可以是一个简单函数的实现。所以,在我们本次的编程训练任务中,Git 至少会提交 2 次或以上。

Part 6. 效能工具介绍

为了测试并改进程序生成四则运算算式的效率,我们需要使用效能分析工具。效能分析工具并不能帮助我们直接改进算法的效率,但它可以帮我们分析找到代码中执行效率最差,也就是所谓 效能瓶颈 的部分。这之后我们就可以把精力花费在改进瓶颈上,从而高效快速地提升程序性能。

Visual Studio 内置了非常棒的效能工具,学名叫做 性能探查器 。点击 IDE 顶部菜单栏中的 分析 ,即可看到 性能探查器

performance probe

我们这里关注在程序的执行效率方面,所以我们选择测试 CPU 使用率 即可。如果想探查内存泄露问题,也可以选择使用其他选项。

add another option

先别急着开始探查。我们的代码目前只产生 1 个四则运算算式,不存在性能问题。我们首先来给代码多加几百万个循环,让它运行 足够长 时间,才能准确测出代码的效能问题。增加循环体后的 main 函数体如下所示

int main()
{
  	for (int i = 0; i < 10000000; i++) {
		  Calculator* calc = new Calculator();
		  string question = calc->MakeFormula();
		  cout << question << endl;
		  string ret = calc->Solve("11+22");
		  cout << ret << endl;
	  }
}

好了,现在让我们开始效能分析。即使程序没有执行完成,效能分析也是可以强行结束的。让程序跑几十秒之后,就可以结束。点击效能分析工具界面左上角的 停止收集 即可停止收集数据。

stop collect

下图就是一份完整的效能分析报告。

performance analysis

从图中我们可以看到,Solve 方法在总 CPU 耗时中占了约 30% 左右,但如果想获得更详细的信息(比如具体到哪一行占了这么久,详细报告阅读起来更易懂),我们需要点击上图中的 创建详细的报告...,创建完成后会自动打开一个后缀为 .vspx 的文件,如下图所示:

get more detail

我们点进 Calculator::Solve 看看,可以看到非常清晰的每行代码占用 CPU 的时间比例,如下图左侧所示。然后我们就可以着手改进代码的效率,比如结合场景用更高效的数据结构,优化一些没有用的代码等等。

check report

Part 7. 提交代码

在完成 Debug单元测试 之后,我们现在来学习一下如何提交代码到 Github 上,并利用 Github 进行团队协作。之前我们已经介绍过了 git addgit commit 命令,但这两条命令只会对本地的仓库进行修改,也就是说之前的所有操作都是离线的。我们要想让 Github 上也跟踪到最新的改变,就需要使用 git push 命令。

在使用该命令前,请确保所有本地的改动都已经 addcommit 了。可以用 git status 来检查:

git status

出现如图所示的 nothing to commit 即说明已经可以 push 了。使用 push 命令后,会弹出一个窗口要求登录 Github,此时输入 Github 的 用户名或邮箱密码 即可成功 push。

login

成功的提示如下所示,其中 master 部分应该是 java / cplusplus。

git push

在完成 push 后,我们就可以开始向源仓库(即阿超的仓库)发起 Pull Request(简称 PR ,指发起请求给仓库贡献代码)。打开你 Fork 后的项目主页,如图所示,点击按钮 New pull request

pull request

如果你按照教程的节奏一步步走下来了,那么在点击 New Pull Request后,应该出现如下所示界面。如果不是,请联系群内的助教/老师,或者在本博客下留言补充错误截图。

此时点击 Create pull request 即可发起请求。等待仓库主人阿超通过审核后,你的代码就可以成功合并进阿超的仓库。至此就完成了整个教程,恭喜!

参考链接:

posted on 2019-02-18 22:02  ChildishChange  阅读(4859)  评论(4编辑  收藏  举报

导航