- 1:项目要求
- 2:设计任务分析
- 3:设计方案
- 3.1:用户类
- 3.2:群组类
- 3.3:程序功能结构
- 3.4:函数调用结构
- 4:详细设计
- 4.1:用户类:
- 4.2:群组类:
- 4.3:文件存储及程序内格式:
- 5:关键设计
- 6:总结与体会
- 6.1:出现的问题:
- 6.2:优点:
- 6.3:缺点:
- 6.4:对本次课程设计的认识:
项目要求
2019软件学院C++课程设计
课程设计目的:
1、熟悉利用面向对象的方法以及C++的编程思想来完成系统的设计;
2、锻炼学生在设计的过程中,建立清晰的类层次,应用继承和多态等面向对象的编程思想;
3、通过本课程设计,加深对面向对象程序设计课程所学知识的理解,熟练掌握和巩固C++语言的基本知识和语法规范,深刻体会面向对象的编程思想,掌握使用面向对象程序设计语言C++,学会编写结构清晰、风格良好的C++语言程序,从而具备利用计算机编程分析解决综合性实际问题的初步能力。
课程设计题目:模拟即时通信系统实现
一、题目描述
基于社交的即时通信是腾*公司的主要业务,先后有QQ、微信、微博等服务,可能还将继续推出微商、微唱、微走、微笑等产品。这些软件既可以独立提供服务,又互相辉映关联。腾*公司希望对各系统进行整合,形成统一的立体社交软件平台。现请完成该平台的设计并实现。要求如下:
1、用户基本信息:
号码ID,昵称,出生时间,T龄(号码申请时间)、所在地、好友列表、群列表。
微博与QQ共享ID,微信采用独立ID,但是可以与QQ号码绑定对应。其他微X产品也分为这两种情况。
2、好友管理
(1)实现各功能好友信息的添加、修改、删除、查询的功能。
(2)可以查询微X之间各自共同好友。如微信可以添加QQ推荐好友。
3、群管理
(1)设定每个微X功能已有1001、1002、1003、1004、1005、1006等群号。
(2)加入群、退出群、挨T、查询群成员等。
(3)不同微X之间群的理念不同,比如:QQ群可以申请加入,而微信群则只能推荐加入;QQ群允许设置临时讨论组(子群),微信群则不允许;QQ群有以群主为核心的管理员制度,而微信群仅有群主为特权账号。
4、开通管理
用户可以选择自己开通该平台的N个微X服务。
5、登录管理
各微X之间只要有一个服务登录,则其它服务简单确认后视为自动登录。
6、功能展示要求(main函数)
(1)设计约定。开通服务情况、群成员信息和好友信息可以预先保存到文件中,在系统启动时将这些信息加载到内存中;
(2)一个服务登录后,本人开通的其它所有服务均进入开通状态。
(3)服务之间可以依据本人开通的任意另外一个服务的好友添加好友。
(4)展示一个服务当前群的特色功能;在群成员数据不受伤害的前提下,动态变换为其他类型群的管理特色。
(5)实现QQ的点对点的TCP通信的收发功能。(选做)提示:
a)需要加载ws2_32.lib静态库,打开头文件winsock.h。
b)百度IP地址、端口等概念;
c)百度socket编程,关注bind、listen、accept、connect、send、receive等函数用法。
二、技术层次要求及说明
1、基本层次。
完成上述功能要求,所采用技术不限,比如采用纯面向过程思想实现;
2、支持对象层次。
正确完成了类的切割,利用对象技术实现。
(1)容器类主要包括:例如,微X成员管理。
(2)其它主要类包括:例如,微X信息、群信息、好友信息。
3、抽象、封装层次
采用了继承或者组合实现复用,对数据成员提供了必要的接口保护;
(1)抽象出了基础类,并被其它功能复用;
(2)如好友维护、群信息维护等操作均应该提供接口形式;
4、面向对象层次
支持多态功能,支持依据设计原则的优化。
好友管理、群管理等;
5、优化提高层次
(1)提供简便菜单,以1、2等数字区分几类功能,并允许返回菜单;
(2)I/O操作支持。基本功能中,已有设定信息,在初始化时候可以固化在程序代码中,也可以存放在文件中,每次容器实例化时读入,析构时写回文件中,以实现断电保存。
(3)可扩展性支持,需要考虑群、好友等与主要服务之间的关系;
(4)灵活性支持。群的管理模式动态可变;
(5)程序有必要的注释;
(6)可以采用UML工具画出简单类图
(7)为防止不诚信行为,要求类的设计均以独立文件存在,且所有的类名称后面应有自己的姓名缩写,如张三设计的QQ信息类名称:TencentZhS。
三、设计步骤(参考 ):
在清楚上述系统功能要处理是什么的基础上,考虑用如下方式来设计
1、确定所需的类及其相互间的关系。
(1)要从问题中归纳出一个概念或实体,从这些概念或实体出发建立相应的类。
(2)尽量使类小而简单,以使其看起来容易理解。
(3)充分利用封装以增加类的可靠性,以便使用时保证更加可靠。
(4)通过继承建立类族,以方便使用多态性。
2、确定每个类的实现。
(1)考虑类的对象应该如何构造和析构。
(2)考虑类的成员函数的建立。
(3)综合考虑各个类在命名和功能方面有哪些共性。
3、细化有关的类,描述他们之间的相互关系,即类关系和对象关系。
4、描述本系统的界面,通过分别定义成员的不同属性,为抽象和实现提供分离的接口。
四、设计工具
1、设计工具:建议使用.net 系列中的C++ 编译器,但不局限于此。
2、不提倡使用MFC和可视化开发技术。
五、设计报告
(报告的具体格式附后)
六、考核方式
1、在设计结束前的最后一天检查程序并接受质疑。
2、检查程序前须提交设计报告(按提交报告的先后顺序检查程序)。
七、考核标准:
参照5个技术层次划分。
八、课程设计后作业(不考核)
引入可视化设计,在本课程设计基础上实现可视化QQ即时通信功能,包括: 多人聊天;聊天记录查询。需要涉及知识如下:
1、网络通信编程;
2、可视化编程;
3、多线程编程;
4、数据库编程;
设计任务分析
腾*公司主打以社交为主要目的的即时通讯业务,已有 QQ、微信、微博服务,将来还要推 出其他的一些服务,现希望实现对这些服务统一管理,使其既可形成统一的社交软件平台,又可 以相互独立的使用。
需要实现:
1. 各应用的用户的基本信息及增删改查,可以关联或绑定不同服务的账号
2. 对各个用户的好友进行增删改查以及推荐共同好友、关联账号的好友等操作,同时实现好友
之间通信的功能
3. 各用户对群组的管理,包括创建群组、增删改查群组、邀请好友加入群组、踢人、
解散群等操作
4. 用户可以自己选择开通哪些服务(如微博等)
5. 对账号及与之关联服务的账号的登陆管理,即同一账号不能重复登陆且一旦登陆,与其管理
服务的账号也自动登陆
6. 程序开始前从 txt 文本或 csv 表格中导入已有的数据,对数据进行修改后即时更新 txt 文本及
csv 表格中的数据,以防程序异常退出造成的数据丢失
设计方案
下面是一些本程序设计的思路和理念
用户类
在用户类的设计方面,首先设计一个名为User_DYf的虚基类,其中包括如ID等所有用户都有的信息数据。QQ用户类,WX用户类各自继承此类,并对应各自情况设计各自的成员函数,如各自的构造函数等
群组类
在群组类的设计方面,设计QQ群类和WX群类,其中QQ群类比WX群类多一个Admin(管理员)的容器(vector<long long>),并各自设计对应函数(图中WX类红线划去的函数为作图错误,因WX类中无Admin,所以无这些函数)。Temporary_Group为临时讨论组类,因其也并无管理员,所以继承了WX类,同时加入group_subid(作为群组下属编号,如群组1001中建立的第1个讨论组的ID为1001,group_subid为1,依此类推)
程序功能结构
上图为本程序所有功能之间的关系,本程序在开始会有选择是否已有帐号,若没有则进行注册,若有则进入登陆界面。登陆后会有菜单(好友、群组、服务、个人信息、搜索、申请消息、注销及退出等选项),选择对应选项后则进入其下属菜单。
函数调用结构
详细设计
用户类:
设计虚基类User_DYf,成员变量分别为ID(帐号),Time birth(生日,Time为定义的一个结构体,内部成员变量分别为year,month,day,后同),Time T_Age(注册年月日),string name(昵称),string location(位置),vector<long long>friends(好友列表),vector<int>groups(群组列表),同时设计获取这些信息的函数。QQ,WX类继承User_DYf,并各自根据文件结构设计构造函数。
群组类:
分别设计QQ群类和WX群类,其中成员变量为(以WX类为例,QQ类比WX类多一个vector<long long>Admin)int ID(群id),string name(群名称),vector<long long>Members(群成员容器),long long owner(群主id)。成员函数设置添加成员,删除成员,成员身份变更(如从普通用户变为群主)等。Temporary_Group为临时讨论组类,因其并无管理员,所以继承了WX类,同时加入group_subid(作为群组下属编号,如群组1001中建立的第1个讨论组的ID为1001,group_subid为1,依此类推)
文件存储及程序内格式:
账户信息(账号密码):
文件内存储格式为“账号,密码”,程序内考虑到帐号和密码是一一对应的且在登陆过后并无调用,所以使用map<long long,string>来存储账号密码。
账号详细信息(用户信息如昵称,生日等):
文件内存储格式为“账号,昵称,生日,注册日期,位置“,程序内信息存储到User_QQ(WX)_DYf中,并将对象存储到容器中。
群详细信息:文件内存储格式为“群ID*群名称*群成员1,群成员2…*群主*管理员1,管理员2…(若无管理员则为群主**,WX中无管理员,以群主结尾)”, 程序内信息存储到Group_QQ(WX)_DYf中,并将对象存储到容器中。
临时讨论组信息:
文件内存储格式为“群组ID*群组下属ID*群名称*群成员1,群成员2…*群主”,程序中存储到Temporary_Group中。
用户好友,群组信息:
文件内存储格式为“A,账号(\n),F,friend1,friend2…(\n),G,group1,group2…(\n)“,程序内则采用vector容器来存储此用户的好友及群组。
好友请求:
文件内存储格式为“申请者账号,被加者账号”,程序内采用map<long long,long long>存储。
群组邀请:
文件内存储格式为“群组ID,邀请者账号,被邀者账号“,程序内采用map<int,vector<vector<long long>>>来存储。
账号服务信息:
文件内存储格式为“账号,微博(0/1)微走(0/1)微笑(0/1)“,程序内采用map<long long,string>存储。
群/用户间聊天信息:
文件内存储格式为“账户$内容$时间(yyyy-mm-dd HH:MM:SS),程序内每条信息采用结构体来存储,结构体内成员为long long send(发送消息者),string content(内容),string chat_time(聊天时间),所有消息存储到vector容器中。
关键设计
本程序中,有几个关键的函数:
1. 数据类型转换函数(包括三个)
上图三个函数作为文件读取数据并存储所需的最基本的函数,在本程序中起到了至关重要的作用
2. 读取函数
上图为两个例子,这段程序能够保证将一行内的信息准确地分割成各个项,这对信息的处理是很有利的
3. 聊天函数中的键盘输入检测函数
因为本程序所使用的操作系统为MacOS Mojave,并无windows中的“conio.h“头文件,也就没有kbhit()这个函数,于是在网上找到了linux上的实现方法。得益于此,聊天程序得以实时刷新消息,可以发送消息,也可按”esc“键退出,而getchar()这类的函数因为阻塞了进程来检测,所以并不能做到上述功能。
4. 判断变量是否已存在于vector中
对于注册账号,邀请好友进群等需要添加数据的情况,这个函数可以很好的保证不会出现重复的数据(如注册时输入的ID已存在或好友已在群中等等)
5. 退出群组保护函数
当退出群组时,如果退出者为群主,退出后此群组会陷入没有群主的情况,这个函数要求群主在退出群组前必须将群主身份移交至另一用户身上,从而避免了这种会导致错误的情况。
6. ID与昵称转换函数
在好友列表中,因获取到的为好友的账号,而不是昵称,这对用户并不友好,而此函数可以将账号转换成名称,大大提升了界面对用户的友好度(程序中还有ID_to_GroupName,既群组ID转换为群名称函数,与此函数同理,在此并未放出)。
总结与体会
出现的问题:
1. 时间安排问题
未能够合理安排时间,每次只在程序设计课那一天或附近几天会接触项 目,而其他时间则没有接触项目,导致最后几天的时间较紧,需要用大量时间去调试和编写程序,效率如下图,可以看出在最后几天代码修改编写量远远高于项目开始前中期。
2. 各种没遇到过的错误
在此次项目中,我遇到了许多之前未曾遇到过的错误,我将它们一一记录了下来,以便之后再次遇到此类情况后能够知道为什么出错,怎么改(下面是4个例子)
1.Cannot jump from switch statement to this case label
出现原因:在switch语句中,case下不可声明变量,否则出现如上错误 解决方法:在switch语句外声明变量,删除case内声明变量语句
2.Thread 1: EXC_BAD_ACCESS (code=1, address=0x0) 出现原因:在录入群组信息时,有一行空行(\n),未经处理存入了群组类,导致群组内容更新后Group_QQ_info.size()比有效信息的size()大1,存储时读取不到Group_QQ_info中成员信息(vector<long long>),引发错误 解决方法:在录入群组信息时,添加一条无效信息判断语句: if(str.size()<10) continue; 即可排除无效信息行
3.Cannot jump from switch statement to this case label 出现原因:在网上查询后,发现使用 switch 语句时,当在 case 中,需要完成给变量赋值等操作时,块定义会创建一个新的作用域,这似乎会干扰编译器正确解释switch语句的能力。(来源:https://stackoverflow.com/questions/42750044/ios-cannot-jump-from-switch-statement-to-this-case-label) 解决方法:如下: switch(option){ case 0: { // Code break; } deflaut: { // Code break; } }
4.Thread 1: EXC_BAD_ACCESS (code=2, address=0x103180000) 出现原因:函数应操作Admin容器,但内部参数复制时未改动(仍然用Member容器的参数[size()等])导致访问出错 解决方法:改正程序内参数 |
优点:
1. 可实现数据的实时更新,即在程序中进行一系列操作后(例如开通账号、增删改查好友、群组等操作)立即更新数据到文件中,这样可以防止因程序错误等异常退出问题导致的信息丢失或损坏
2. 聊天可以做到实时刷新消息,不用手动刷新数据,也不会因检测键盘输入而阻塞程序运行
3. 特别对数据的合理性进行了保护,如之前提到的退出群组保护函数等,使得数据更符合逻辑,程序出错的概率也更少
缺点:
1. 程序过于繁琐,好多函数可以简化,但是在设计之初并未考虑完全,后期时间也并不够。
2. 并未实现TCP点对点通信,因未知原因导致客户端在连接服务器时收到服务器发来的消息但未能成功解读出其中的内容(仅有信号没有内容)
3. 因MacOS Mojave的未知原因,程序编译所生成的可执行文件皆无法使用相对路径来访问文件,只能通过绝对路径来访问,这是一种很不好的习惯(至今仍未解决)
对本次课程设计的认识:
本次课程设计让我更加深入地了解了C++面向对象的意义,锻炼了面向面向对象编程的思维方式。在设计程序时,重新温习了之前学习的继承等相关内容,使我对其有了更深刻的认识。在实现的过程中,遇到了许许多多的问题,程序也调试了许久,其中不乏粗心大意导致的疏漏,但更多的是对一些关键知识点掌握的不扎实,通过本次课程设计,我加强了编程的基本功,夯实了编程基础,也开拓了视野,相信经过本次课程设计后,我的代码会比以前更加简洁有效,思维也比原来更加开阔。
因源码过长此处不放出
程序压缩文件:Analog instant messaging system implementation
程序设计报告:程序设计报告——模拟即时通信系统
嘿嘿嘿