10371-2面向对象课程设计指导

更新时间:2024-03-30 10:40:01 阅读量: 综合文库 文档下载

说明:文章内容仅供预览,部分内容可能不全。下载后的文档,内容与下面显示的完全一致。下载之前请确认下面内容是否您想要的,是否完整无缺。

面向对象课程设计指导

专业:电子信息科学与技术 指导教师:程磊、黄迎春

一、课程设计的目的

面向对象课程设计是电子信息科学与技术专业非常重要的实践性环节之一,是学完面向对象程序设计课程后的一次全面的综合练习。本课程设计主要在于巩固学生对面向对象程序设计的基础理论的理解,掌握面向对象程序设计开发的基本方法,进一步提高学生综合运用所学知识的能力。

二、课程设计的内容及要求

以Microsoft Visual C++ 6.0作为集成开发环境,完成面向对象课程设计。要求每人完成以下一个题目,题目由指导教师指派,学生进行程序分析、设计、编程与调试、功能测试,并最终完成课程设计报告,课程设计题目如下:

PART I

1.分数类的设计与实现

2.一维数组类模板的设计与实现 3.向量类的设计与实现 4.多项式类的设计与实现

5.设计一个能够实现n×n矩阵操作的类 6.复数类的设计与实现

7.链表类(模版)的设计与实现 8.大整数类的设计与实现 9.多边形类虚函数设计

10.小型人员信息管理系统的设计与实现 11.大学教师工资的计算与显示 12.教师与干部信息管理 13.哈希表类模板的设计与实现 14.赫夫曼编码的设计与实现 15.二叉树类模板的设计与实现 16.队列类模板的设计与实现

17.栈类模板的设计与实现 18.表达式求值的设计与实现 19.字符串类的设计与实现 20.简单加密系统设计 21.某系学生成绩管理系统 22.制作一个通讯录程序 23.设计一个库存管理系统 24.设计INI配置文件分析器 25.二维点容器设计 26.集合并、交和差的运算 27.自定义文件操作类MyFile设计 PART II 1.计算器程序 2.小型数字钟 3.象棋程序

4.设计一个歌友会售票系统 5.设计一个打地鼠小游戏 6.设计一个贪吃蛇小游戏 7. 设计一个小型考试系统 8.弹球小游戏 9.生命游戏

题目分成C++语言基本应用和MFC应用两部分,每名学生必须完成第一部分和第二部分中各一道题。第一部分题目已经按学号分配好,第二部分由学生自行选择并完成(注意:第二部分题目要独立完成,杜绝雷同情况)。

三、课程设计时间安排

本课程设计在学完面向对象程序设计课程后进行,具体时间为2周。

四、课程设计考核办法

(1)课程设计报告

不少于5000字,报告除了在封面中应有题目、班级、姓名、学号和课程设计日期以外,其正文一般包括需求分析、类与对象设计、算法设计、调试问题分析、用户使用说明、参考

文献等部分。

(2)程序演示和验收答辩情况

在课程设计的后期,指导教师在实验室进行课程设计程序的验收与答辩,由学生演示编制的程序,并回答教师提出的问题,教师检查学生程序的编写情况。

(3)课程设计的考勤与纪律遵守情况

五、各题目的具体要求 PART I

1.分数类的设计与实现

建立用于完成分数形式运算的类RationalNumber。编写一个测试该类的程序。用整数变量表示类的私有数据(即分子和分母)。给类提供一个能够对所声明的对象初始化的构造函数。为了能够在不提供初始化值的情况下也能对对象初始化,构造函数中应该包含默认的值。构造函数还应该以最简分数的形式存储数据,即2/4应该在对象中存储成分子为1、分母为2的形式。公有成员函数应该有以下功能:

(1) 两个有理数相加,以最简形式保存结果; (2) 两个有理数相减,以最简形式保存结果; (3) 两个有理数相乘,以最简形式保存结果; (4) 两个有理数相除,以最简形式保存结果; (5) 以a/b的形式输出有理数(a是分子,b是分母); (6) 以浮点形式输出有理数。

2.一维数组类模板的设计与实现

建立一维数组数据结构的模板类,使一维数组中的数据元素可以是char, int, float等多种数据类型,类中的成员函数主要包括:

(1) 排序函数,能够对数组元素进行升序排列;

(2) 查找函数,能够在输入待查元素后,输出其在数组中的下标;

(3) 构造函数,初始化输入数组元素,这里要求数组元素的个数n是一个变量; (4) 析构函数,释放数组元素所占用的堆内存; (5) Set函数,可以为指定的数组元素赋值; (6) Get函数,可以读取指定数组元素的值; (7) 重载数组访问运算符“[ ]”; (8) Print函数,可以输出数组元素的值。 (9) 编写一个测试该模板类的程序。

3.向量类的设计与实现

编写一个实现n维向量各种操作的类,功能包括:

(1) 构造函数实现n维向量的初始化构造,这里n可变; (2) 析构函数实现向量动态内存的释放; (3) 拷贝构造函数实现向量的拷贝构造;

(4) 重载赋值运算符‘=’,实现两个向量之间的赋值; (5) 编写成员函数求两个向量的内积; (6) 编写成员函数求两个向量的外积; (7) 编写成员函数求两个向量的和; (8) 编写成员函数求两个向量的差;

(9) 编写成员函数判断两个向量之间的线性相关性。

(10) 编写一个主函数测试多项式类的上述功能。 可以自行编写链表类,或者用数组,或者使用STL中的list容器,也可以采用vector类实现。

4.多项式类的设计与实现

开发多项式类Polynomial,多项式的每一项用链表的结点表示,每项包含一个系数和一个指数。例如:2x的指数为4,系数为2。请开发一个完整的Polynomial类,包括构造函数、析构函数以及“get”函数(读取值)和“set”函数(设置值)。该类还要提供以下重载的运算符:

(1) 重载加法运算符+,将两个多项式相加。 (2) 重载加法运算符-,将两个多项式相减。

(3) 重载赋值运算符=,将一个多项式赋给另外一个多项式。 (4) 重载加法运算符*,将两个多项式相乘。

(5) 编写一个主函数测试多项式类的上述功能。

可以自行编写链表类,或者使用STL中的list容器,也可以采用vector类实现。

4

5.设计一个能够实现n×n矩阵操作的类

这里的n可变,主要功能包括:

(1) 使用构造函数完成方阵的初始化赋值(动态内存分配); (2) 使用析构函数完成矩阵动态内存的释放; (3) 重载加法运算符+,实现两个矩阵的和; (4) 重载加法运算符-,实现两个矩阵的差; (5) 重载加法运算符*,实现两个矩阵的积; (6) 重载加法运算符=,实现两个矩阵之间的赋值; (7) 使用函数实现矩阵的转置; (8) 使用函数求矩阵中的最大值;

(9) 使用函数求矩阵中的最小值。

(10) 添加函数Det以得到矩阵对应行列式的值

(11) 重载加法运算符<<,实现矩阵按照行列的格式输出 (12) 编写一个主函数测试上述功能。

6.复数类的设计与实现

设计一个复数类,复数类的数据成员是real和imag,它们的数据类型是double,分别表示复数的实部和虚部,要求如下:

(1) 编写一个带有缺省参数的构造函数,默认值为(0,0),实现一个复数的构造。 (2) 编写一个拷贝构造函数,实现一个复数的拷贝构造。

(3) 编写一个析构函数,在函数中输出(real,imag)被析构,例如:如果复数的

实部为1,虚部为2,则被析构时程序输出:“(1,2)被析构”。

(4) 重载运算符“+”为复数类的成员函数,其功能是返回两个复数的相加的结果。 (5) 重载运算符“-”为复数类的成员函数,其功能是返回两个复数的相减的结果。 (6) 重载运算符“*”为复数类的友元函数,其功能是返回两个复数的相乘的结果。 (7) 重载运算符“/”为复数类的友元函数,其功能是返回两个复数的相除的结果。 (8) 重载单目运算符“-”为复数类的友元函数,其功能是返回当前复数的相反数。 (9) 重载运算符“>>”为复数类的友元函数,其功能是按照以格式(real,imag) (10) 输入复数。例如:如果复数的实部为1,虚部为2,则输入的格式是“(1,2)”。 (11) 重载运算符“<<”为复数类的友元函数,其功能是按照以格式(real,imag) (12) 输出复数。例如:如果复数的实部为1,虚部为2,则输出的结果是“(1,2)”。 (13) 以上函数在类的内部写出函数原型,在类的外部写出函数实现代码,最后编写

一个主函数main测试以上的函数功能。

7.链表类(模版)的设计与实现

编写一个实现学生信息单链表各种操作的类,学生信息包括学号、姓名和成绩,类实现以下功能:

(1) 初始化单链表为空表;

(2) 实现单链表的插入操作的成员函数; (3) 实现单链表的删除操作的成员函数;

(4) 实现单链表的查找操作的成员函数(给定学号,查找其学号、姓名和成绩); (5) 实现求单链表长度的成员函数;

(6) 实现建立单链表的成员函数,单链表节点的个数不确定; (7) 重载运算符“+=”实现两个链表对象合并功能;

(8) 编写Unique()成员函数,实现剔除链表中重复元素,使所有节点值唯一; (9) 编写一个主函数测试上述功能。

8.大整数类的设计与实现

计算机中表示整数的位数是有限的,设计并实现一个能够进行任意长度整数准确计算的类,完成以下功能:

(1) 用构造函数实现大整数的构造。

(2) 重载“+”运算符,实现两个大整数的相加运算; (3) 重载“-”运算符,实现两个大整数的相减运算; (4) 重载“*”运算符,实现两个大整数的相乘运算; (5) 重载“<<”运算符,实现大整数的输出。

提示:长整数用一维字符型数组来存储,数组的每一个元素顺序存储长整数的一位数字。 设有k位长整数m,用数组a[]存储:

m=a[k]*10^(k-1)+a[k-1]*10^(k-2)+...+a[2]*10^1+a[1]*10^0

并用a[0]存储长整数m的位数,即a[0]=k。

可以自行编写链表类,或者使用STL中的list容器,也可以采用vector类实现。

9.多边形类虚函数设计

设计多边形基类CPolygon及其派生类CTriangle、CRectangel和CCircle类,为各个类设计虚函数Input()、Area()、Print(),实现从数据文件读入数据,从而生成各种多边形对象,并且计算面积和输出对象信息,数据文件格式如下: TRIANGLE 底边长 高

RECTANGLE 长 宽 CIRCLE 半径

每一行存储一个对象,第一单词位对象类型,其后是数据,具体实例: TRIANGLE 30 5 RECTANGLE 12 7 TRIANGLE 20 3

CIRCLE 27

CIRCLE 8 ??????

要求采用STL中的vector来存储基类指针来指向各个对象,并用循环实现各形状对象的操作。

10.小型人员信息管理系统的设计与实现

某小型公司主要有四类人员:经理、兼职技术人员、销售经理和兼职推销员。现在,需要存储这些人员的姓名、编号、级别、当月薪水,计算月薪总额并显示全部信息。

人员编号基数为1000,每输入一个人员信息编号顺序加1。

程序要有对所有人员提升级别的功能。本例中为简单起见,所有人员的初始级别均为1级,然后进行升级,经理升为4级,兼职技术人员和销售经理升为3级,推销员认为1级。

月薪计算办法是:经理拿固定月薪8000元;兼职技术人员按每小时100元领取月薪;

兼职推销员的月薪按该推销员当月销售额的4%提成;销售经理即拿固定月薪也领取销售提成,固定月薪为5000元,销售提成为所管辖部门当月销售总额的千分之五。

要求为每一类人员单独建立文件,并在每个文件中手工录入一些人员基本信息,并根据这些基本信息计算职工的月薪,并将计算结果保存入相应的磁盘文件中。要求编写程序实现上述功能,并且可以通过实例演示上述功能。

11.大学教师工资的计算与显示

某大学的教师的职称等级决定其工资等级,教师共有四种职称等级:助教,讲师,副教授,教授,其月薪分别为:助教900元/月,讲师1000元/月,副教授1300元/月,教授1600元/月。编写一个计算教师工资的程序,要求如下:

(1)建立一个抽象基类Teacher,其数据成员有:教师姓名name,教师职称position,教师工资Salary;成员函数有:纯虚函数CalcSalary( )计算教师工资,纯虚函数Show( )输出教师的姓名、职称和工资。

(2)分别从抽象基类Teacher中派生出四个具体类Assistant,Lecture,AssociateProfessor,Professor,分别表示助教,讲师,副教授,教授。在每一个类中实现三个成员函数,构造函数为教师初始化姓名和职称;CalcSalary( )函数计算教师工资;Show( )输出教师的姓名、类别和工资,其中输出格式为:

姓名:name,职称:position,工资:Salary元

(3)教师的姓名和职称信息包含在磁盘文件teacher.txt中,要求建立teacher.txt文本文件,并输入如下信息:

王刚 助教 李铭 讲师, 张莉 副教授 赵蒙 教授

程序从Teacher.txt文件中读取上述信息,作为工资计算依据。

(4)编写一个主函数main测试以上各类,要求用一个基类的指针分别指向派生类对象分别计算每一个人的工资,并将输出结果保存到Teacher.txt文件中。例如输出如下结果:

王刚 助教900 李铭 讲师 1000 张莉 副教授 1300 赵蒙 教授 1600

12.教师与干部信息管理

分别定义Teacher(教师)类和Cadre(干部)类,采用多重继承方式由这两个类派生出新类Teacher_Cadre(教师兼干部)。要求:

(1) 在两个基类中都包含姓名、年龄、性别、地址、电话等数据成员。

(2) 在Teacher类中还包含数据成员title(职称),在Cadre类中还包含数据成员

post(职务),在Techear_Cadre类中还包含数据成员wages(工资)。

(3) 对两个基类中的姓名、年龄、性别、地址、电话等数据成员用相同的名字,在

引用这些数据成员时,指定作用域。

(4) 在类体中声明成员函数,在类外定义成员函数。

(5) 在派生类Teacher_Cadre的成员函数show中调用Teacher类中的display函数,

输出姓名、年龄、性别、职称、地址、电话,然后再用cout语句输出职务与工资。

(6) 人员的基本信息分别存储在磁盘文件Teacher.txt,Cadre.txt和

Teacher_Cadre.txt文件中,Teacher.txt的格式是:(姓名、年龄、性别、地址、电话、职称),Cadre.txt的格式是:(姓名、年龄、性别、地址、电话、职务),Teacher_Cadre.txt的格式是:(姓名、年龄、性别、地址、电话、职称、职务、工资),要求在操作系统中建立上述文件,并按照上述格式手工录入几条记录,程序首先从文件中读取相应数据,然后完成上述功能。

13.哈希表类模板的设计与实现

实现哈希表类模板,数据元素可以是char, int, float等多种数据类型,包括以下功能:

(1) 实现哈希表的建立,散列函数采用除留余数法; (2) 使用开放地址法处理冲突; (3) 使用链地址法处理冲突; (4) 实现哈希表元素的插入; (5) 实现哈希表元素的删除; (6) 实现哈希表的查找;

(7) 将上述功能作为类的成员函数实现,编写主函数测试上述查找功能。

14.赫夫曼编码的设计与实现

进行赫夫曼编码类的设计并实现,包括以下功能:

(1) 设计类的数据成员和成员函数,实现赫夫曼树的存储; (2) 根据给定的通信字符出现的概率,实现赫夫曼树的建立; (3) 遍历赫夫曼树,求赫夫曼编码; (4) 给出一段字符串,进行赫夫曼编码;

(5) 将上述功能作为类的成员函数实现,编写主函数测试上述功能。

15.二叉树类模板的设计与实现

进行二叉树类模板的设计并实现,数据元素可以是char, int, float等多种数据类型,包括以下功能:

(1) 采用顺序存储结构或链式存储结构实现二叉树的存储; (2) 实现二叉树的建树;

(3) 实现二叉树的前序、中序、后序遍历; (4) 能够求解二叉树的结点总数和叶子结点总数; (5) 能够求解二叉树的高度;

(6) 将上述功能作为类的成员函数实现,编写主函数测试上述功能。

16.队列类模板的设计与实现

进行队列类模板的设计并实现,队列采用循环队列实现,数据元素可以是char, int, float等多种数据类型,包括以下功能:

(1) 实现初始化队列操作,建立一个空队列; (2) 实现清空队列操作;

(3) 实现判断队列是否为空的操作; (4) 实现求队列长度的操作; (5) 实现返回队首元素的操作; (6) 实现入队操作; (7) 实现出队操作;

(8) 实现队列的遍历操作,输出队列的每个元素。

(9) 将上述功能作为类的成员函数实现,编写主函数测试上述功能。

17.栈类模板的设计与实现

进行栈类模板的设计并实现,栈采用链式存储结构,数据元素可以是char, int, float等多种数据类型,包括以下功能:

(1) 实现初始化栈操作,建立一个空栈; (2) 实现清空栈操作;

(3) 实现判断栈是否为空的操作; (4) 实现求栈长度的操作; (5) 实现返回栈顶元素的操作; (6) 实现入栈操作; (7) 实现出栈操作;

(8) 实现栈的遍历操作,输出栈的每个元素。

(9) 将上述功能作为类的成员函数实现,编写主函数测试上述功能。

18.表达式求值的设计与实现

表达式求值是程序设计语言编译中的一个最基本问题,要求进行类的设计与实现,采用算符优先法实现表达式求值。具体要求如下:

(1) 用顺序栈作为表达式求值过程中运算符栈和操作数栈的实现; (2) 用二维数组存储算符间的优先关系; (3) 采用算符优先法实现表达式求值;

(4) 将上述功能作为类的成员函数实现,编写主函数测试上述功能。

19.字符串类的设计与实现

封装字符串类,实现字符初始化、存储、对象赋值、求长度、连接、查找子串(字符)、插入子串(字符)、比较、清空串等操作,其中赋值、连接要用运算符重载。(用动态内存分配实现)。

例:CMyString str1(“Hello ”),str2;

str2=str1; //赋值,str2 也变成 “Hello”

str2+=” China”; //连接功能,str2 变成 “Hello China” str2.Find(?o?) //返回4

str1.Insert(2,?K?); //str1 变成 HeKllo

str1.Compair(“HeMllo”); //返回1,”HeKllo”〉”HeMllo”,向等返回0。

20.简单加密系统设计

实现输入明文字符串及密码后,使用加密功能可以得到密文字符串,同样输入密文和密码后可以得到明文字符串。加密方法可以对字符的ASCII码进行异或操作来实现,如C=A + B ,且 B = C +○○ A,A为密码,B为明文,C为密文,+○为异或操作。 要求:及加密类、解密类,可以操作文件。可以用控制台窗口(DOS)实现,也可以用MFC设计可视化程序(推荐)。

21.某系学生成绩管理系统

要求,实现年级管理(4个年级)、班级管理及学生信息管理,可以实现班级的查询、添加、删除、修改,学生成绩的查询、添加、删除、修改等。 程序使用类的包含方法实现。

(1)一个班最多30名学生,4门课程;

(2)班级信息、学生成绩存放在数据文件中(可以多个文件);

(3)内存中数据组织建议采用STL容器。

设计班级类、学生类等,建立文件、输出文件内容、计算每个学生总分并进行总分排序、查找各科最低分和最高分。可以用控制台窗口(DOS)实现,也可以用MFC设计可视化程序(推荐)。

22.制作一个通讯录程序。

(1)该程序具有查找、添加、修改、删除功能;

(2)通讯录包括:姓名、电话、街道、城市、省、邮编; (3)内存中数据组织建议采用STL容器;

(4)建立文件,存储文件可以使用默认文件名或指定文件名;

(5)存储信息到文件中,从文件中读取信息。

程序说明:通讯录用记录类来实现,查找、删除使用姓名作为关键字,删除可以删除一人,

封 皮

(按学校要求手工填写)

题目:1. 用全选主元高斯消去法解线性方程组 2. 弹球小游戏 学院:信息科学与工程

专业:电子信息科学与技术 学号:1003070XXX 姓名:XXX

指导教师:程磊,黄迎春

课 程 设 计 任 务 书

学院 学生姓名 设计题目 内容及要求: 信息科学与工程 XXX 专业 学号 电子信息科学与技术 0803070XXX 用全选主元高斯消去法解线性方程组/弹球小游戏 PART I.用全选主元高斯消去法解线性方程组 很多自然科学和工程技术中的问题的解决最终都归结到线性方程组的求解,高斯消去法是线性方程组解法中很经典的算法,由它改进、变形得到的全选主元消去法,是一种效率很高、较为常用的线性方程组解法。 要求采用C++语言实现线性方程组的求解,具体要求如下: (1)进行类的设计,实现线性方程组的存储与操作。 (2)方程组的求解采用全选主元高斯消去法。 (3)编写主函数测试程序的功能。 PART II 弹球小游戏 ????????????????? 进度安排: 第17周:分析题目,查阅课题相关资料,进行类设计、算法设计; 第18周:上机调试程序,程序测试与分析,撰写课程设计报告,准备答辩。 指导教师(签字): 年 月 日 学院院长(签字) 年 月 日

目 录

PART I

1 需求分析 ....................................................................................... 19 2 算法基本原理 ............................................................................... 19 3 类设计........................................................................................... 20 4 详细设计 ....................................................................................... 21

4.1 类的接口设计.................................................................................................... 22 4.2 类的实现............................................................................................................ 23 4.3 主函数设计........................................................................................................ 28

5 运行结果与分析 ............................................................................ 29

5.1 程序运行结果.................................................................................................... 29 5.2运行结果分析 .................................................................................................... 31

PART II

类设计 详细设计

6 参考文献 ....................................................................................... 31

PART I 1 需求分析

(1)很多自然科学和工程技术中的问题的解决最终都归结到线性方程组的求解,高斯消去法是线性方程组解法中很经典的算法,由它改进、变形得到的全选主元消去法,是一种效率很高、较为常用的线性方程组解法。

(2)线性方程组的一般形式为Ax=b,其中A是线性方程组的系数矩阵,x是列向量,是方程组的解,b也是列向量,这里假定A是非奇异矩阵。

(3)程序测试数据来自徐士良先生编著的《C常用算法程序集》中,所选的方程是:

?0.2368x0?0.2471x1?0.2568??0.1968x0?0.2071x1?1.2168??0.1581x0?1.1675x1?0.1768?1.1161x?0.1245x?0.139701?x2?1.2671x3?1.8471x2?0.2271x3?1.7471x2?0.1871x3?1.6471x2?0.1490x3?1.54712 算法基本原理

设有n元线性方程组:

?a00x0?a01x1???a0,n?1xn?1?b0??a10x0?a11x1???a1,n?1xn?1?b1????a?n?1,0x0?an?1,1x1???an?1,n?1xn?1?bn?1(1)

将(1)写成矩阵形式Ax?b,

?a00?a10A??????an?1,0a01a11?an?1,1????a0,n??a1,n?,???an?1,n?1??x0???x1?,x???????x?n?1??b0???b1?b???????b?n?1?(2)

将系数矩阵A和向量b放在一起,形成增广矩阵B:

?a00?a10B?(A,b)??????an?1,0a01a11?an?1,1????b0??a1,nb1?????an?1,n?1bn?1?a0,n19

(3)

全选主元消去就在这个B矩阵上进行,整个过程分为两个步骤: 第一步,消去过程。

对于k从0开始到n-2结束,进行以下三步:

(1)首先,从系数矩阵A的第k行、k列开始的子矩阵中选取绝对值最大的元素作为主元素,例如:

ai1,j1?maxaij?0k?i?nk?j?n然后交换B的第k行与第i1行,第k行与第j1列,这样,这个子矩阵中的具有最大绝对值的元素被交换到第k行、k列的位置。

(2)其次,进行归一化计算。计算方法为:

(3)最后,进行消去运算:

aij?akj?aikakj,j,i?k?1,k?2,?,n?1akj?akj/akk,j?k?1,k?2,?,n?1bk?bk/akkbi?bi?aikbk,i?k?1,k?2,?,n?1第二步,回代过程。

xn?1?bn?1/an?1,n?1n?1bi?bi??aj?i?1ijxj,i?n?2,n?1,?,0在这里,只是列出简要地给出了全选主元高斯消去法的算法步骤,具体推导及详细过程可参考数值分析方面的有关资料。

3 类设计

从上面的算法分析可以看到,本设计面临的计算问题的关键是矩阵运算。可以定义一个矩阵类Matrix作为基类,然后由矩阵类派生出线性方程组类Linequ。矩阵类Matrix只处理n×n类型的方阵,方阵用一个一维数组来存放,矩阵类Matrix的数据成员包括数组的首地址和n,矩阵类Matrix的功能有设置矩阵的值SetMatrix()和显示矩阵PrintM()等。

从问题的需要来看,线性方程组类Linequ的数据除了由矩阵类Matrix继承过来用于存放系数矩阵A的成员外,还应该包括存放解向量x和方程右端向量b

20

的数组首地址。线性方程组类Linequ的主要操作有设置SetLinequ()、显示PrintL()、求解Solve()及输出方程的解showX()。可以通过定义线性方程组类Linequ的新增成员函数来实现这些针对方程组求解的功能。

矩阵类Matrix和线性方程组类Linequ的组成及相互关系如图1所示。

- sums :double*- solu :double*+ Linequ(dims: int=2)+ ~Linequ()+ SetLinequ(a: double*,b: double*) :void+ PrintL() :void+ Solve() :int + ShoeX() :void+ Matrix(dims: int=2)+ ~Matrix()+ SetMatrix(rmatr: double*) :void+ PrintM() :void# index :int# Matrix :double*Matrixlinequ 图1 Matrix类和Linequ类的派生关系的UML图形表示

在线性方程组的求解过程中,在线性方程组类Linequ的成员函数Solve中需要访问基类矩阵类Matrix的数据成员,利用公有继承方式派生,同时将Matrix类中的数据成员的访问控制设置为保护类型。这样,经过公有派生之后,基类的保护成员在派生类中依然是保护成员,可以被派生类的成员函数访问。

4 详细设计

整个程序分为三个独立的文档,Linequ.h文件中包括矩阵类Matrix和线性方程组类Linequ的声明,Linequ.cpp文件中包括这两个类的成员函数实现文件;main.cpp文件包括程序的主函数,主函数中定义了一个类Linequ的对象,通过这个对象求解一个四元线性方程组。

21

4.1 类的接口设计

//Linequ.h文件,实现类的声明 #include #include using namespace std;

class Matrix { public: };

class Linequ:public Matrix { public: };

经过公有派生,Linequ类获得了除构造函数、析构函数之外的Matrix类的全部成员,由于基类的成员是公有和保护类型,因此在派生类中的成员函数中,

22

//基类Matrix声明

//外部接口

//构造函数 //析构函数

//矩阵赋初值

Matrix(int dims=2); ~Matrix();

void SetMatrix(double *rmax); void PrintM(); int index;

//显示矩阵

protected:

//方阵的行数

//矩阵存放数组首地址

double* MatrixA;

//公有派生类Linequ声明 //外部接口 //构造函数 //析构函数 //显示方程 //显示方程的解 //方程右端项 //方程的解

Linequ(int dims=2); ~Linequ(); void PrintL(); int Solve();

void SetLinequ(double *a, double *b); //方程赋值

//全选主元高斯消去法求解方程 //私有数据

void ShowX(); double *sums; double *solu;

private:

基类继承来的成员全部可以访问,而对于建立Linequ类对象的外部模块来讲,基类的保护成员是无法访问的。通过保护访问类型和公有的继承方式,实现了基类Matrix的数据的有效共享和可靠保护。在程序中,方程的系数矩阵、解以及右端项全部采用了动态内存分配技术,这些工作都是在基类、派生类的构造函数中完成,它们的清理工作在析构函数中完成。

4.2 类的实现

//Linequ.cpp文件,类实现 #include \ //包含类的声明头文件

//Matrix类的实现

void Matrix::SetMatrix(double *rmatr) //设置矩阵

{ for(int i=0;i

MatrixA[i]=rmatr[i];

//矩阵成员赋初值

}

Matrix::Matrix(int dims) //矩阵Matrix类的构造函数 { index=dims;

//矩阵行数赋值

MatrixA=new double[index*index]; //动态内存分配 }

Matrix::~Matrix()

//矩阵Matrix类的析构函数

{ delete[] MatrixA;

//内存释放 }

void Matrix::PrintM() //显示矩阵元素

{ cout<<\ for (int i=0;i

{

23

for (int j=0;j

cout<<*(MatrixA+i*index+j)<<\ cout<

}

}

//派生类Linequ的实现

Linequ::Linequ(int dims):Matrix(dims) //派生类Linequ的构造函数 {

//使用参数调用基类构造函数 sums=new double[dims]; //动态内存分配 solu=new double[dims];

}

Linequ::~Linequ() //派生类Linequ的析构函数 { delete[] sums; //释放内存

delete[] solu;

}

void Linequ::SetLinequ(double *a,double *b) //设置线性方程组 { SetMatrix(a); //调用基类函数

for (int i=0;i

sums[i]=b[i]; }

void Linequ::PrintL() //显示线性方程组

{ cout<<\ for (int i=0;i

cout<<*(MatrixA+i*index+j)<<\

cout<<\ \

24

}

}

void Linequ::ShowX() //输出方程组的解 { cout<<\ for (int i=0;i

}

}

int Linequ::Solve() //全选主元高斯法求解方程

{ int *js,l,k,i,j,is,p,q; double d,t; double *MatrixB; //声明局部矩阵MatrixB

MatrixB=new double[index*index]; //将矩阵MatrixA赋值给MatrixB for (i=0;i

MatrixB[i]=MatrixA[i];

js=new int[index]; //分配动态内存

l=1;

for (k=0;k<=index-2;k++)

//消去过程

{ d=0.0;

for (i=k;i<=index-1;i++) //选取主元素 for (j=k;j<=index-1;j++) { t=fabs(MatrixB[i*index+j]); if (t>d)

{ d=t; js[k]=j; is=i; }

}

25

if (d+1.0==1.0) //主元素为零

l=0; else

//主元素交换

{ if(js[k]!=k) for(i=0;i<=index-1;i++) { p=i*index+k; q=i*index+js[k];

t=MatrixB[p]; MatrixB[p]=MatrixB[q]; MatrixB[q]=t;

}

if (is!=k)

{ for (j=k;j<=index-1;j++) { p=k*index+j; q=is*index+j;

t=MatrixB[p]; MatrixB[p]=MatrixB[q]; MatrixB[q]=t;

}

t=sums[k]; sums[k]=sums[is]; sums[is]=t;

} }

if(l==0) //若主元素为零,求解失败 { delete[]js;

cout<<\ return 0;

}

d=MatrixB[k*index+k]; //归一化计算 for (j=k+1;j<=index-1;j++) { p=k*index+j;

MatrixB[p]=MatrixB[p]/d; }

26

sums[k]=sums[k]/d; for (i=k+1;i<=index-1;i++) //消去计算

{ for(j=k+1;j<=index-1;j++) {

p=index*i+j;

MatrixB[p]=MatrixB[p]-MatrixB[i*index+k]*MatrixB[k*index+j];

}

sums[i]=sums[i]-MatrixB[i*index+k]*sums[k]; }

}

d=MatrixB[(index-1)*index+index-1]; if(fabs(d)+1.0==1.0) { delete[] js; cout<<\ return 0;

}

solu[index-1]=sums[index-1]/d; //回代过程

for (i=index-2;i>=0;i--) { t=0.0;

for (j=i+1;j<=index-1;j++)

t=t+MatrixB[i*index+j]*solu[j]; solu[i]=sums[i]-t;

}

js[index-1]=index-1; for (k=index-1;k>=0;k--) if(js[k]!=k) { t=solu[k]; solu[k]=solu[js[k]]; solu[js[k]]=t;

}

27

}

delete []js; return 1;

在类的成员函数实现过程中,派生类的构造函数使用参数调用了基类的构造函数,为矩阵动态分配了内存空间。而派生类的析构函数同样也调用了基类的析构函数,只是整个调用过程中完全是由系统内部完成。基类的保护数据成员,经过公有派生之后,在派生类中是以保护成员的身份出现的,派生类的成员函数可以自由地进行访问。

全选主元高斯消去法求解函数返回值为整数,正常完成之后,返回值为1,非正常结束后,返回值为0,根据函数的返回值,就可以判断求解过程的完成情况。

4.3 主函数设计

//main.cpp主函数 #include \

int main() //主函数 {

double a[]= { };

double b[4]={ 1.8471,1.7471,1.6471,1.5471}; //方程右端项 Linequ equ1(4); //定义一个四元方程组对象 equ1.SetLinequ(a,b); //设置方程组 equ1.PrintL();

//输出方程组

if(equ1.Solve()) //求解方程组

equ1.ShowX(); //输出方程组的解 cout<<\

//求解失败

else

0.2368,0.2471,0.2568,1.2671, 0.1968,0.2071,1.2168,0.2271, 0.1581,1.1675,0.1768,0.1871, 1.1161,0.1254,0.1397,0.1490

//系数矩阵

28

}

return 1;

在程序的主函数部分,选择了一个四元方程组作为一个实际例子来验证算法。方程组的系数及右端项数据都使用一维数组来存储。首先定义一个四元方程组对象equ1,在定义过程中调用派生类的构造函数,通过派生类的构造函数,又调用了基类的构造函数,对进一步求解动态分配了内存。接着给方程组的系数和右端项赋初值,把我们选定的方程组输入到新定义的方程组对象equ1中。对象成员函数PrintL、Solve和ShowX分别完成了输出方程组、求解方程组和输出求解结果的任务。

5 运行结果与分析

5.1 程序运行结果

程序运行结果如图2所示。

图2 程序运行结果

从图2中可以看出,程序能够实现全选主元高斯消去法对于线性方程组的求解,但是,对于求解结果的正确性问题却无法获知,为了能够验证求解结果的正确性,考虑将求解结果x带入原方程Ax=b中,如果满足原方程,即说明求解结果是正确的,否则,说明求解存在问题,需对程序进行进一步调试分析。

为此,考虑在Linequ类中增加测试函数Test,用以验证求解结果的正确性。 void Linequ::test()

//求解结果验证函数

29

{ }

在主函数main中增加语句: equ1.test(); //验证求解结果

经过验证的程序运行结果如图3所示。

图3 程序运行结果的验证

double *b2;

b2=new double[index];

for (int i=0;i

for (i=0;i

cout<

cout<

b2[i]=0;

for (int j=0;j

b2[i]=b2[i]+MatrixA[i*index+j]*solu[j];

从图3中可以看出,方程组求解验证的右端项结果与原右端项结果完全一致,这说明了方程组求解的正确性。

30

5.2运行结果分析

整个程序中的矩阵存储采用的是一维数组和动态内存分配方式。

基类是专门处理矩阵的类,公有派生类Linequ是针对线性方程组而设计的,除了继承基类的基本特征之外,结合问题的实际需要,增加了很多线性方程组所特有的成员,使基类Matrix进一步具体化、特殊化,达到对问题的有效描述和处理。

程序的访问控制也是根据问题的需要而设计的。基类的数据成员的存储、维护着矩阵数据,这正是派生类方程组的系数矩阵,使派生类解方程成员函数必须访问的。利用保护成员特征,将基类数据成员的访问控制属性设置为保护型,在公有派生类Linequ中就可以访问到基类继承下来的保护成员;而对于类外的其余模块,这些数据无法访问。这样,就在数据的共享与隐藏之间寻找到一个比较恰当的结合点。

在派生过程中,基类的构造函数和析构函数无法继承下来,因此在派生类中需要添加构造函数、析构函数来完成派生类的初始化和最后清理工作。派生类的构造函数通过调用基类的构造函数来对基类数据进行初始化,本设计中,派生类Linequ的构造函数调用了基类Matrix的构造函数并传递必须的初始化参数。派生类的析构函数调用基类的构造函数,共同完成清理任务。

6 参考文献

1.徐士良。C常用算法程序集。北京:清华大学出版社,1995

2.郑莉,董渊,张瑞丰。C++语言程序设计(第3版)。北京:清华大学出版社,2007

3.钱能。C++程序设计教程(第二版)。北京:清华大学出版社,2007 4.陈志泊,王春玲。面向对象的程序设计语言——C++。北京:人民邮电出版社,2002

5.李庆扬,王能超,易大义。数值分析。湖北:华中理工大学出版社,1986

31

5.2运行结果分析

整个程序中的矩阵存储采用的是一维数组和动态内存分配方式。

基类是专门处理矩阵的类,公有派生类Linequ是针对线性方程组而设计的,除了继承基类的基本特征之外,结合问题的实际需要,增加了很多线性方程组所特有的成员,使基类Matrix进一步具体化、特殊化,达到对问题的有效描述和处理。

程序的访问控制也是根据问题的需要而设计的。基类的数据成员的存储、维护着矩阵数据,这正是派生类方程组的系数矩阵,使派生类解方程成员函数必须访问的。利用保护成员特征,将基类数据成员的访问控制属性设置为保护型,在公有派生类Linequ中就可以访问到基类继承下来的保护成员;而对于类外的其余模块,这些数据无法访问。这样,就在数据的共享与隐藏之间寻找到一个比较恰当的结合点。

在派生过程中,基类的构造函数和析构函数无法继承下来,因此在派生类中需要添加构造函数、析构函数来完成派生类的初始化和最后清理工作。派生类的构造函数通过调用基类的构造函数来对基类数据进行初始化,本设计中,派生类Linequ的构造函数调用了基类Matrix的构造函数并传递必须的初始化参数。派生类的析构函数调用基类的构造函数,共同完成清理任务。

6 参考文献

1.徐士良。C常用算法程序集。北京:清华大学出版社,1995

2.郑莉,董渊,张瑞丰。C++语言程序设计(第3版)。北京:清华大学出版社,2007

3.钱能。C++程序设计教程(第二版)。北京:清华大学出版社,2007 4.陈志泊,王春玲。面向对象的程序设计语言——C++。北京:人民邮电出版社,2002

5.李庆扬,王能超,易大义。数值分析。湖北:华中理工大学出版社,1986

31

本文来源:https://www.bwwdw.com/article/p3qr.html

Top