一、课程设计目的
实践能力是工程技术人才必须具备的能力。本课程设计是为《面向对象程序设计》课程设置的实践教学环节,目的是培养学生综合运用面向对象程序设计的基本理论、方法和相关专业知识分析、解决实际问题的能力。
1.帮助学生系统掌握《面向对象程序设计》课程的主要内容。
2.通过课程设计的综合训练,提高学生的编程和动手能力。
3.培养学生综合运用所学的理论知识,以及独立分析和解决实际问题的能力。
二、课程设计题目
我爱记单词系统
我爱记单词是一款帮助大家记忆英文单词的软件,软件功能如下:
(1)单词库导入
单词信息包括:单词编号、单词英文、单词中文解释等基本信息。
提供文件方式存储的单词列表,从文件读入单词,建立单词库。
(2)学习计划设置
①设置每天需要记忆的单词数,根据总的单词数,计算出计划完成的天数。
②设置单词学习的顺序,按照词表顺序或者随机产生每天需学习的单词。
(3)单词学习
①学习新单词
学习时,每个单词给出中英文对照(或者只给出英文,中文先隐藏起来,给出选择是否给出中文对照),一个一个的浏览,直到当天计划完成。在浏览单词的过程中,如果发现单词没有办法一次记住,添加到生词表中。
其中涉及到已记忆单词表,未记忆单词表和生词表。将每天已经学习过的单词添加到已记忆单词表中,并从整个单词表中删除,形成未记忆单词表。记忆过程中个人需要重点记忆的单词添加到生词表中。
②复习生词表
按照天或者整个浏览生词表。
扩展功能:
(1)支持单词库的选择。
(2)支持系统退出时,将已记忆单词表,未记忆单词表和生词表记录到文件中。
(3)显示打卡记录。
三、相关类的设计
1.WordNode类
WordNode类用来表示一条单词记录。对于词典中的每条单词记录,有英语单词和中文解释两个内容,用两个私有数据成员来进行表示:
Private:
string english; //英文单词
string chinese; //对应的中文翻译
同时为了方便其他类对单词记录进行操作,还提供了一系列比较简单基础的公有操作:
public:
WordNode(); //默认空构造函数
WordNode(string english, string chinese); //给出英文单词和中文解释的构造函数
string getEnglish(); //获取英文内容
string getChinese(); //获取中文内容
void setEnglish(string english); //重新设置英文内容
void setChinese(string chinese); //重新设置中文内容
2.DataBase类:
DataBase类是一个存放数据的类,用来储存生词本中的内容。单词记录放在一个vector中:
private:
vector<WordNode> words; //存放单词,向量内的元素为WordNode类对象
WordNode wordNode;
另外提供对外的接口,对生词表进行一系列操作:
public:
DataBase();//默认构造函数
int getDicSize(); //返回单词的总数目
bool isEmpty();//判断字典是否为空
WordNode getWordNode(int i); //返回下标为i的单词,i不合法则返回空
int setWordNode(int i,WordNode newWordNode); //修改第i个WordNode节点,失败则返回值为负数
int addWordNode(WordNode wordNode); //添加一个新单词
int deleteWordNode(int i); //删除第i个WordNode节点,失败则返回值为负数
int binarySearch(string word); //根据单词二分查找相应的条目,查找失败返回负数
void sortWords(); //重新对单词按字典序进行排序
3.myless类:
myless类是为了方便调用sort()函数,重载()运算符,保证生词本中的记录按照词典顺序来排列的,参数是两个WordNode类的对象,按照英语单词的词典顺序排列:
public:
bool operator()(WordNode a, WordNode b) {
return a.getEnglish() < b.getEnglish(); //按照字典序排列
}
4.MyFrame类
MyFrame类主要控制系统的界面,有一个DateBase的实例dateBase为私有的数据成员,用来保存生词本记录数据。
同时MyFrame类提供了一系列公有方法来实现与背单词相关的功能。如下:
public:
MyFrame(); //空构造函数
void initialize(); //初始化整个界面
void addWord(); //添加新单词
void deleteWord(); //删除已有的单词
void modifyWord(); //修改现有的单词
void browseDic(); //浏览词典
void exitSystem(); //退出系统
void exam(); //模拟考试
void exercise(); //背单词练习
void showall(); //显示生词表单词
void printBlankLines(int n); //控制格式,输出一些空行
四、主要功能的实现
1.单词节点及生词本的实现
单词节点主要是通过WordNode类来实现的,每一条单词记录对应一个WordNode类的实例。
另外在DataBase类的构造函数中借助ifstream实现构建单词库的功能,首先实例化一个ifstream类,然后打开文件,读入相应的单词记录同时实例化一个WordNode类,最后将该对象放入vector容器中并关闭文件:
ifstream file("生词本.txt"); //打开生词本文件
while(file>>english>>chinese){
this->wordNode.setChinese(chinese);
this->wordNode.setEnglish(english);
(this->words).push_back(this->wordNode); //将相应节点插入数据库中
}
file.close(); //关闭文件
2.增、删、改功能的实现
增加单词节点对应于DataBase类中的addWordNode(addWordNode wordNode)函数,是通过节点放到容器vector中并重新排序实现的:
int DataBase::addWordNode(WordNode wordNode){
this->words.push_back(wordNode);
this->sortWords();
return 0;
}
删除节点,首先需要根据给出的单词进行二分查找,然后通过DataBase类中的deleteWordNode(int i)函数来实现,二分查找的基本原理是利用序列的有序性,每次将查找范围折半,直到确定找到目标或者目标不存在:
//删除第i个WordNode节点,失败则返回值为负数
int DataBase::deleteWordNode(int i){
if((i<0) || (i>=(int)(this->words.size()))){
//cout<<"下标不合法!"<<endl;
return -1;
}
this->words.erase((this->words.begin())+i);
return 1;
}
//根据单词二分查找相应的条目,查找失败返回空
int DataBase::binarySearch(string word){
int left=0;
int right=this->getDicSize()-1;
//进行二分查找
while(left<=right){
int middle=(left+right)/2; //中间元素
if(this->words[middle].getEnglish() == word) //找到目标
return middle;
else if(this->words[middle].getEnglish() > word) //目标在左半部分
right=middle-1;
else left=middle+1; //目标在右半部分
}
return -1;
//cout<<"查找失败!"<<endl;
//return *(new WordNode());
}
修改单词的功能也只需找到对应的节点,然后修改翻译部分的中文内容即可,由于单词的内容没有发生变化,因此不需要重新排序。
3.单词学习功能
单词学习功能的实现其实比较简单,首先由用户选择学习的顺序,随机顺序或者是词典顺序。若为随机顺序,通过srand(unsigned(time(0)));设置时间种子,然后生成随机数取出相应单词呈现给用户,同时用户还可以选择是否中英文对照,或是只显示英文。
int number=rand()%(this->dataBase.getDicSize());//随机顺序
cout<<"单词: "<<this->dataBase.getWordNode(number).getEnglish()<<endl;
cout<<endl;
if(way==2){
cout<<"解释: "<<this->dataBase.getWordNode(number).getChinese()<<endl;
}
for(i=0;i<length;i++){ //词典顺序
system("cls");
int flag=0;
cout<<"单词: "<<this->dataBase.getWordNode(i).getEnglish()<<endl;
cout<<endl;
if(way==2){
cout<<"解释: "<<this->dataBase.getWordNode(i).getChinese()<<endl;
}
如果是词典顺序,则用户可以设置每次需要记忆的单词数,根据单词的总数,计算出计划完成的天数。另外,在学习单词的过程中,用户也可以选择删除将已记忆单词从生词本中删除。
五、程序清单(源程序)
1.//WoedNode.h
#ifndef WORDNODE_H
#define WORDNODE_H
#include<iostream>
#include<string>
#include<vector>
using namespace std;
class WordNode{
public:
//默认空构造函数
WordNode();
//给出英文单词和中文解释的构造函数
WordNode(string english, string chinese);
//获取英文内容
string getEnglish();
//获取中文内容
string getChinese();
//重新设置英文内容
void setEnglish(string english);
//重新设置中文内容
void setChinese(string chinese);
private:
//英文单词
string english;
//对应的中文翻译
string chinese;
};
#endif
2.//WordNode.cpp
#include<iostream>
#include<string>
#include<vector>
#include "WordNode.h"
using namespace std;
//默认空构造函数
WordNode::WordNode(){
}
//给出英文单词和中文解释的构造函数
WordNode::WordNode(string english, string chinese){
this->english=english;
this->chinese=chinese;
}
//获取英文内容
string WordNode::getEnglish(){
return this->english;
}
//获取中文内容
string WordNode::getChinese(){
return this->chinese;
}
//重新设置英文内容
void WordNode::setEnglish(string english){
this->english=english;
}
//重新设置中文翻译
void WordNode::setChinese(string chinese){
this->chinese=chinese;
}
3.//myless.cpp
#include<iostream>
#include<string>
#include "WordNode.h"
using namespace std;
//用来辅助排序
class myless
{
public:
//重载运算符,为排序做准备
bool operator()(WordNode a, WordNode b) {
//按照字典序排列
return a.getEnglish() < b.getEnglish();
}
};
4.//DataBase.h
#ifndef DATABASE_H
#define DATABASE_H
#include<iostream>
#include<string>
#include<vector>
#include<algorithm>
#include "WordNode.h"
using namespace std;
//存放与单词相关的数据
class DataBase{
public:
//默认构造函数
DataBase();
//返回单词的总数目
int getDicSize();
//判断字典是否为空
bool isEmpty();
//返回下标为i的单词,i不合法则返回空
WordNode getWordNode(int i);
//修改第i个WordNode节点,失败则返回值为负数
int setWordNode(int i,WordNode newWordNode);
//添加一个新单词
int addWordNode(WordNode wordNode);
//删除第i个WordNode节点,失败则返回值为负数
int deleteWordNode(int i);
//根据单词二分查找相应的条目,查找失败返回负数
int binarySearch(string word);
//重新对单词按字典序进行排序
void sortWords();
private:
//存放单词,向量内的元素为WordNode类对象
vector<WordNode> words;
WordNode wordNode;
};
#endif
5.//DataBase.cpp
#include<iostream>
#include<string>
#include<vector>
#include<algorithm>
#include<fstream>
#include "DataBase.h"
#include "WordNode.h"
#include "myless.cpp"
using namespace std;
//默认构造函数
DataBase::DataBase(){
string english;
string chinese;
ifstream file("生词本.txt"); //打开词典文件
while(file>>english>>chinese){
this->wordNode.setChinese(chinese);
this->wordNode.setEnglish(english);
(this->words).push_back(this->wordNode); //将相应节点插入数据库中
}
file.close(); //关闭文件
}
//返回单词的总数目
int DataBase::getDicSize(){
return this->words.size();
}
//判断字典是否为空
bool DataBase::isEmpty(){
if((this->getDicSize())<=0)
return true;
return false;
}
//返回下标为i的单词,i不合法则返回空
WordNode DataBase::getWordNode(int i){
if(i<0 || (i>= (this->getDicSize()))){
cout<<"下标不合法!"<<endl;
return *(new WordNode());
}
return this->words[i];
}
//修改第i个WordNode节点,失败则返回值为负数
int DataBase::setWordNode(int i,WordNode newWordNode){
if((i<0) || (i>=this->getDicSize())){
cout<<"下标不合法!"<<endl;
return -1;
}
this->words[i]=newWordNode;
this->sortWords();
return 1;
}
//添加一个新单词
int DataBase::addWordNode(WordNode wordNode){
this->words.push_back(wordNode);
this->sortWords();
return 0;
}
//删除第i个WordNode节点,失败则返回值为负数
int DataBase::deleteWordNode(int i){
if((i<0) || (i>=(int)(this->words.size()))){
//cout<<"下标不合法!"<<endl;
return -1;
}
this->words.erase((this->words.begin())+i);
return 1;
}
//根据单词二分查找相应的条目,查找失败返回空
int DataBase::binarySearch(string word){
int left=0;
int right=this->getDicSize()-1;
//进行二分查找
while(left<=right){
//中间元素
int middle=(left+right)/2;
//找到目标
if(this->words[middle].getEnglish() == word)
return middle;
//目标在左半部分
else if(this->words[middle].getEnglish() > word)
right=middle-1;
//目标在右半部分
else left=middle+1;
}
return -1;
//cout<<"查找失败!"<<endl;
//return *(new WordNode());
}
//重新对单词按字典序进行排序
void DataBase::sortWords(){
sort(this->words.begin(), this->words.end(), myless());
return;
}
6.//MyFrame.h
#ifndef MYFRAME_H
#define MYFRAME_H
#include<iostream>
#include <fstream>
#include<string>
#include<vector>
#include<iomanip>
#include "DataBase.h"
#include "WordNode.h"
using namespace std;
//与前台显示有关的功能在该类中声明并实现
class MyFrame{
public:
//空构造函数
MyFrame(); //创建生词本文件
//初始化整个界面
void initialize();
//添加新单词
void addWord();
//删除已有的单词
void deleteWord();
//修改现有的单词
void modifyWord();
//浏览词典
void browseDic();
//退出系统
void exitSystem();
//模拟考试
void exam();
//背单词练习
void exercise();
//控制格式,输出一些空行
void printBlankLines(int n);
private:
//相关的内容数据库
DataBase dataBase;
};
#endif
7.//MyFrame.cpp
#include<iostream>
#include<string>
#include<vector>
#include<ctime>
#include<cstring>
#include<iomanip>
#include "MyFrame.h"
using namespace std;
//空构造函数
MyFrame::MyFrame(){
}
//初始化整个界面
void MyFrame::initialize(){
int order;
bool valid=true;
while(1){ //循环执行
system("cls");
cout << "------------------------------" << endl;
cout << "|欢迎使用我爱记单词系统 |" << endl;
cout << "|1.添加单词 |" << endl;
cout << "|2.删除单词 |" << endl;
cout << "|3.修改单词 |" << endl;
cout << "|4.单词练习 |" << endl;
cout << "|5.查看生词本 |" << endl;
cout << "|6.退出系统 |" << endl;
cout << "------------------------------" << endl;
this->printBlankLines(2);
if(valid == true) //判断上次输入是否有效
cout<<"请输入您的选择:";
else cout<<"输入无效,请重新输入:";
cin>>order;
if((order>0) && (order<7)) //判断输入是否有效
valid=true;
else{ //无效则需要重新输入
valid=false;
system("cls");
continue;
}
//根据用户输入选择不同功能
if(order == 1)
this->addWord();
else if(order == 2)
this->deleteWord();
else if(order == 3)
this->modifyWord();
else if(order == 4)
this->exercise();
else if(order == 5)
this->browseDic();
else if(order == 6)
this->exitSystem();
}
}
void MyFrame::addWord(){
system("cls");
int k=0;
cout<< "***************************"<<endl;
cout<< "请添加新词,输入'='表示结束"<<endl;
while(1){
string english;
string chinese;
this->printBlankLines(2);
cout<<"请输入待添加的新单词:";
cin>>english;
//查找该单词是否已经存在
if(english=="="){
system("cls");
if(k>=1){
cout<<"\n单词导入词库成功,本次共导入"<<k<<"个单词"<<endl;
this->printBlankLines(3);
}else{
cout<<"\n 您没有导入任何单词!!"<<endl;
this->printBlankLines(3);
}
system("pause");
return ;
}
if(this->dataBase.binarySearch(english)>=0){
cout<<"单词"<<english<<"已经存在!"<<endl;
cout<<"添加失败"<<endl;
}
else{
cout<<"请输入单词的中文解释:";
cin>>chinese;
WordNode wordNode(english,chinese);
this->dataBase.addWordNode(wordNode);
cout<<"成功添加单词!"<<endl;
k++;
}
this->dataBase.sortWords(); //重新对单词进行排序
}
system("pause");
return;
}
//删除已有的单词
void MyFrame::deleteWord(){
string english;
string chinese;
system("cls");
int length=this->dataBase.getDicSize();
this->showWord( length);
this->printBlankLines(3);
cout<<"请输入要删除的单词:";
cin>>english;
int number=this->dataBase.binarySearch(english); //查找相应的单词是否存在
if(number<0){ //单词不存在
cout<<"单词"<<english<<"不存在!"<<endl;
cout<<"删除失败!"<<endl;
system("pause");
return;
}
else{ //查找到了相应单词
this->dataBase.deleteWordNode(number);
cout<<"成功删除单词!"<<endl;
system("pause");
return;
}
return;
}
//修改现有的单词
void MyFrame::modifyWord(){
string english;
string chinese;
system("cls");
int length=this->dataBase.getDicSize();
this->showWord( length);
this->printBlankLines(3);
cout<<"请输入要修改的单词:";
cin>>english;
int number=this->dataBase.binarySearch(english); //二分查找相应的单词
if(number<0){
cout<<"单词"<<english<<"不存在!"<<endl;
cout<<"修改单词失败!"<<endl;
system("pause");
return;
}
else{
cout<<"请输入新的解释:";
cin>>chinese;
WordNode wordNode(english,chinese);
this->dataBase.setWordNode(number,wordNode); //修改相应的单词
cout<<"成功修改单词!"<<endl;
system("pause");
return;
}
return;
}
//浏览生词表
void MyFrame::browseDic(){
int order;
system("cls");
this->printBlankLines(5);
//输出提示信息
cout<<" 1.单个查询 "<<endl;
cout<<" 2.浏览全部 "<<endl;
this->printBlankLines(3);
cout<<"请选择浏览方式:";
cin>>order;
if(!(order>=1 && order<=2)){ //检查输入的有效性
cout<<"输入无效!"<<endl;
system("pause");
return;
}
//分不同输入提供不同功能
if(order == 1){
system("cls");
this->printBlankLines(3);
cout<<"请输入要查询的单词:";
string english;
int number;
cin>>english;
number=this->dataBase.binarySearch(english);
if(number < 0){
cout<<"找不到单词"<<english<<endl;
system("pause");
return;
}
else{
cout<<endl;
cout<<"释义:"<<this->dataBase.getWordNode(number).getChinese()<<endl;
this->printBlankLines(3);
system("pause");
return;
}
}
else if(order == 2){
system("cls");
int length=this->dataBase.getDicSize();
this->showWord(length);
system("pause");
return;
}
return;
}
void MyFrame::exercise(){
srand(unsigned(time(0)));
int order;
int way;
int i=0;
int length;
system("cls");
this->printBlankLines(5);
//输出提示信息
cout<<" 1.随机顺序 "<<endl;
cout<<" 2.词表顺序 "<<endl;
this->printBlankLines(3);
cout<<"请选择学习顺序:";
cin>>order;
if(!(order>=1 && order<=2)){
cout<<"输入无效!"<<endl;
system("pause");
return;
}
if(order==2){
system("cls");
this->printBlankLines(5);
cout<<" 请输入本次需要记忆的单词数:";
cin>>length;
if((length<=0) || length >this->dataBase.getDicSize()){
cout<<"输入无效!无法满足查看要求!"<<endl;
system("pause");
return;
}
this->printBlankLines(3);
cout<<" 完成单词练习需要"<<this->dataBase.getDicSize() /length<<"天";
this->printBlankLines(3);
system("pause");
}
system("cls");
this->printBlankLines(5);
cout<<" 1.只英文 "<<endl;
cout<<" 2.中英文对照 "<<endl;
this->printBlankLines(3);
cout<<"请选择学习方式:";
cin>>way;
if(!(way>=1 && way<=2)){
cout<<"输入无效!"<<endl;
system("pause");
return;
}
while(1){
char cmd[100];
system("cls");
this->printBlankLines(5);
//随机顺序
if(order==1){
int flag=0;
int number=rand()%(this->dataBase.getDicSize());
cout<<"单词: "<<this->dataBase.getWordNode(number).getEnglish()<<endl;
cout<<endl;
if(way==2){
cout<<"翻译: "<<this->dataBase.getWordNode(number).getChinese()<<endl;
}
this->printBlankLines(5);
cout<<"输入 ' = '退出练习,按回车键可查看下一个单词";
cin.getline(cmd,100);
if(strcmp(cmd,"=")==0){ //退出背单词的模式
system("cls");
return;
}
else
continue;
}
//词典顺序
else if(order==2){
for(i=0;i<length;i++){
system("cls");
int flag=0;
this->printBlankLines(5);
cout<<"单词: "<<this->dataBase.getWordNode(i).getEnglish()<<endl;
cout<<endl;
if(way==2){
cout<<"翻译: "<<this->dataBase.getWordNode(i).getChinese()<<endl;
}
this->printBlankLines(5);
cout<<" 是否从词库中删除此单词呢?(1.是/0.否) "<<endl;
cin>>flag;
if(flag==1){
this->dataBase.deleteWordNode(i);
cout<<"成功删除单词!"<<endl;
}
this->printBlankLines(3);
system("pause");
}
this->printBlankLines(10);
system("cls");
cout<<"\n本次学习任务完成"<<endl;
this->printBlankLines(3);
system("pause");
return;
}
}
return;
}
//退出系统
void MyFrame::exitSystem(){
exit(0);
}
//显示生词本全部单词
void MyFrame::showWord(int length)
{
cout << " ----------+-----------" << endl;
cout << "|" << setw(10) << "单词";
cout << "|" << setw(10) << "翻译";
cout << "|" << endl;
cout << " ----------+-----------" << endl;
for(int i=0;i<length;i++){
cout<< "|" << setw(10) <<this->dataBase.getWordNode(i).getEnglish();
cout<< "|" << setw(10) <<this->dataBase.getWordNode(i).getChinese();
cout << "|" << endl;
cout << " ----------+-----------" << endl;
}
}
//控制格式,输出一些空行
void MyFrame::printBlankLines(int n){
for(int i=0;i<n;i++)
cout<<endl;
return;
}
六、运行效果截图
1.初始化界面
2.添加单词
3.删除单词
4.修改单词
5.单词学习
5.查看生词本
(1)单个查询
(2)浏览全部
六、课程设计的收获与体会
回想整个设计过程:1.首先弄清楚程序所需要的实现的基本功能,也就是单词库的导入,单词的学习,还有对单词的修改、删除,浏览词库等功能。2.其次是要设计针对特定功能的类,在本次设计中,单词采用一个WordNode为节点进行存储,为了保证每个类的功能清晰、简洁,在每个类内部实现的功能彼此联系,非对外的接口设置为private属性。3.对于程序具体实现,算法主要有二分查找单词、词库的排序等,并且还使用STL辅助。没有什么算法难度,但要保证程序清晰。4.最后是程序的调试,针对测试出的问题不断进行改正,得到最终的代码,但仍不够完善。
通过本次面向对象的课程设计,一方面锻炼了分析问题、类设计的能力,另一方面对于STL中提供的库函数有了初步的了解。本次设计基本实现背单词的功能,但仍然还有许多改进的余地。比如支持系统退出时,将已记忆单词表记录到文件中,并且显示打卡记录,等等这些构想尚未实现。
总体来说,收获还是比较大的,进一步体会到了C++作为一门程序设计语言在较大项目管理中的优势,清晰的结构增减代码的易读性,也非常有助于提高系统的可拓展性。
七、参考文献
c++语言程序设计/郑莉,董渊,何江舟编著.—4版.—北京:清华大学出版社,2010.7(2019.11重印)