面向对象的程序设计实验教学指导书.docx
《面向对象的程序设计实验教学指导书.docx》由会员分享,可在线阅读,更多相关《面向对象的程序设计实验教学指导书.docx(30页珍藏版)》请在冰点文库上搜索。
面向对象的程序设计实验教学指导书
《面向对象程序设计课程》
实验教学指导书
山东协和职业技术学院
计算机学院
二○○七年十二月
前言
一、实验总体目标
《面向对象程序设计》课程在计算机科学与技术专业教学计划中,是一门重要的理论联系实际的专业基础课。
其主要任务是使学生掌握OOP的编程思想和方法、能运用C++语言,在VisualC++的IDE环境下编写各种应用程序。
实验课是本课程重要的教学环节,其目的是使学生掌握OOP的编程思想和方法,C++语言的语法特点、程序结构、主要技术及编程技巧,能在VisualC++的IDE环境下编写各种Win32应用程序,使学生学以致用,提高学生的实践动手能力以及分析问题、解决问题的能力。
二、适用专业年级
适用专业:
计算机软件
三、先修课程
计算机文化基础,C++语言程序设计
四、实验项目及课时分配
实验项目
实验要求
实验类型
每组人数
实验学时
实验一
练习设计、使用类并熟悉编程环境
必修
上机设计
4
2
实验二
多态性练习
必修
上机设计
4
4
实验三
函数的应用
必修
上机设计
4
4
实验四
类与对象
必修
上机设计
4
4
实验五
运算符的重载
必修
上机设计
4
4
实验六
综合练习造
必修
上机设计
4
2
(说明:
实验要求填必修或选修;实验类型填演示性、综合性、验证性、设计性或研究创新性。
)
五、实验环境
微机,MicrosoftVisual6.0
六、实验总体要求
通过该实验要求学生掌握C++程序设计语言与面向对象程序设计的基本概念和方法,具有使用C++语言的编程能力,具有采用面向对象程序设计方法解决实际问题的能力。
为《数据结构》、《信息系统分析与设计》、《电子商务系统分析与设计》等后续课程打下良好基础。
七、本课程实验的重点、难点及教学方法建议
1、按照课程有关章节内容要求,预先编写出程序;
2、上机输入、编译、调试、运行程序直到得到正确结果;
3、分析总结编程方法
实验一 :
练习设计、使用类并熟悉编程环境
一、实验目的:
学会定义类
学会构造类的方法
领会面向对象程序设计的方法
熟悉编程环境
二、实验内容:
(1) 设计一个用来表示直角坐标系上点的位置的Location类,然后在主程序中创建两个对象A和B,要求A在第三象限,B在第二象限,计算给定两点之间的距离并按如下格式输出结果:
A(x1,y1),B(x2,y2)
Distance=d
其中x1,y1,x2,y2为指定值,d为计算结果。
分析:
这是一个很简单的类的设计。
首先我们要清楚这个类要有几个成员和成员函数。
一个点起码要有X坐标和Y坐标,所以我们给其添加两个私有成员;接着要有起码的构造函数和析构函数;最后根据题目要求,我们在设计一个成员函数,用来求两点之间的距离。
程序源代码如下:
程序头文件放在Location.h中
//Location.h
//这是输入输出流所要包含的头文件
#include
//这是要进行数学计算所要包含的头文件
#include
classLocation
{
//横坐标和纵坐标:
private:
intX,Y;
public:
Location(inta,intb);//构造函数
doubledistance(Location&);
//求两点距离的函数
~Location();//析构函数
};
//在构造函数中对类的成员进行初始化
Location:
:
Location(inta,intb)
{
X=a;
Y=b; }
//求两点的距离
//这里我们用的公式是,两点之间的
//距离=
//(x1,y1)和(x2,y2)为两点的坐标
doubleLocation:
:
distance(Location&loc1)
{
doublelength:
length=sqrt((loc1.X-Location:
:
X)**2+
(loc1.Y-Location:
:
Y)**2));
//注意这里的作用域符号:
:
表示X和Y是属//于Location类的
returnlength; }
//由于我们在这个程序里没有用
//new来开辟内存,所以析构函数为空
Location:
:
~Location()
{ }
再在Location.cpp文件中编写主程序
//Location.cpp
#include
#include
#include
voidmain()
{
//先定义Location类的两个对象A和B
//并调用构造函数对其进行初始化
LocationA(-10,-20),B(-30,40);
//输出A(x,y),B(x,y)
cout<<"A("<<<"B("<//输出distance=?
cout<<"Distance="<}
注意:
.h文件和.cpp文件要放在同一个目录下面,如果不是放在同一个目录下面的话,那么在.cpp文件中
要在#include语句中加上Location.h文件所在地的整个目录名,例如:
#include
\Borland\user\location\Location.h>,这样的话,编译时才会不出错。
三、实验思考:
大家可以用指针和引用来改写这个程序,然后和原来的程序比较,看有什么区别。
把这个Location类的对象作为Rectangle类的对象的一个成员,即坐标平面上的一个矩形由一个位置和矩形的长和宽决定。
设计矩形类Rectangle并输出给定点x1和y1的值及长和宽的值。
//先定义类和类的成员函数
//文件名为Rectange.h
//由于Rectangle类中要有Location类的对象
//所以必须包括Location类的声明文件
#include
#include
#include
classRectange
{
private:
//定义矩形长和宽
intlength,width;
//定义矩形左上点
Locationupleft;
public:
//定义矩形的构造函数和析构函数
Rectange(inta,intb,intlength,intwidth);
~Rectange();
};
Rectangle:
:
Rectangle(inta,intb,intlen,intwid)
{
//初始化Rectangle类的length和width
length=len;
width=wid;
//这里通过调用Location类的构造函数
//来初始化Rectangle类的对象upleft
upleft=newLocation(a,b);
}
Rectangle:
:
~Rectangle()
{
deleteupleft;}
//在构造函数中用new开辟了内存空间upleft,
//所以在析构函数中用delete删除它
}
再在CPP文件中编写主程序代码:
//Rectangle.cpp
//包括下面三个头文件的原因在前面已讲过
#include
#include
#include
voidmain()
{
Rectangle A(-10,-20,60,40);
//定义了一个长为60,宽为40,左上点为(-10,-20)
//的矩形,下面将其输出
cout<<"矩形左上点为:
"<<"("<<//A.upleft是Location的成员对象,所以A.upleft.x
//表示输出的是左上点的x坐标,A.upleft.y表示输//出的是左上点的y坐标,即-10和-20
cout<<"矩形的长为:
"<//输出为60
cout<<"矩形的宽为:
"<//输出为40
}
(3) 熟悉C++环境。
大家在编织程序的过程中,要尽量的熟悉BorlandC++的编程环境,了解BC中关于路径的设置,对于主菜单的各个命令要熟悉它的操作过程,编完程序后可以对程序进行小的修改,用来了解程序的流程,以及各个命令的功能。
实验二 :
多态性练习
一、实验目的:
掌握由继承和虚函数获得多态性的方法;
学会利用成员函数访问类的对象的私有成员;
进一步掌握类的设计,熟悉C++编程。
二、实验内容:
(1) 用虚函数设计一个描述正方形的类Square,并具有计算面积的成员函数GetArea()。
从Square类派生一个矩形类Rectangle,Rectangle也使用GetArea()函数计算面积。
分析:
这是一个类继承问题。
先设计一个正方形类,定义一个protected对象edge作为正方形的边长;然后把成员函数GetArea()设计成虚函数,因为派生类Rectangle也要用到GetArea()来求矩形的面积;然后是构造函数和析构函数。
接着设计矩形派生类Rectangle,并公有继承基类Square,矩形派生类的成员函数GetArea()用来求矩形面积。
其中代码的分析在源程序给出。
头文件SquareRectangle.h源代码如下:
//SquareRectangle.h
#include
#include
classSquare
{
protected:
intedge;
//edge为protected对象,所以派生类公有继承基类
//Square时,edge也为派生类的protected对象,派
//生类可直接调用。
public:
virtual doubleGetArea();
//定义为虚函数,所以派生类调用它时
//采用动态联编,易于编程人员编写
Square(intedg);
~Square()
{}
};
Square:
:
Square(intedg)
{
edge=edg; }
//求正方形的面积
doubleSquare:
:
GetArea()
{
doubleresult;
result=edge*edge;
retureresult;
}
//下面的全局函数是专门为了
//动态调用虚函数而设计的
//函数体的声明在源代码的最后面
void objGetArea(Square*base);
classRectangle:
publicSquare
{
protected:
int length;
//它的宽由正方形类Square的成员提供
public:
doubleGetArea();
Rectangle(intedg,intleng);
~Rectangle()
{ }
};
//求矩形的面积
doubleRectangle:
:
GetArea()
{
doubleresult;
result=length*edge;
returnresult;
}
//这里由于edge是公有继承基类Square的
//protected对象,因此可直接调用。
Rectangle:
:
Rectangle(intedg,intleng):
Square(intedg)
{
//这里采用成员列表的形式初始化类的成员
//Square(intedg)调用基类的构造函数
//使edge=edg;
length=leng;
}
voidobjGetArea(Square*obj)
{
obj->GetArea();
}
在SquareRectangle.cpp中给出主函数main()的源代码:
//SquareRectangle.cpp
#include
#include #include
main()
{
Square Squa1(100);
//调用Square:
:
Square(int)初始化
RectangleRect(200,300);
//调用Rectangle:
:
Rectangle(int,int)初始化
//比较下面两个语句
cout<<"正方形Squa1(100)的
面积为:
"<//输出为:
10000
cout<<"正方形Squa1(100)的
面积为:
"<//输出为:
10000
//比较下面两个语句
cout<<"派生矩形Rect(200,300)的
面积为:
:
"<//输出为60000
cout<<"派生矩形Rect(200,300)的
面积为:
:
"<//输出为60000
}
注意:
使用虚函数要注意的地方是,不要在引用虚函数的时候,采用作用符:
或者是:
:
,这样就可能会变成静态联编,根本不能表现虚函数的优越性。
所以我们在调用虚函数时,通常采用的是指针或引用。
就像是上例中的全局函数objGetArea(Square*obj)一样,编译系统会根据实际情况确定是哪一个类在调用虚函数,如果是采用作用符,就失去了虚函数的意义了,就像在主程序中要求比较的四个语句一样,其中一个是动态连编,一个是静态连编。
另外,参数传递时要注意一一对应,在调用objGetArea(Square*)时,一定要在类Squa或Rect前面加&,表示是类Squa或Rect的地址,这样才与指针相对应。
三、实验思考:
除了用全局函数来调用虚函数外,大家还能用别的方法吗?
试编写一例。
实验三 :
运算符的重载
一、实验目的:
了解运算俯重载的基础知识;
学会运算符重载的编程。
二、实验内容:
为complex复数类重载+、++以及*三个友元运算符,并在主程序中进行简单的验算,要求要考虑周全。
分析:
大家只要了解了运算符重载的语法,就不难编出程序了。
下面给出源代码:
类的声明文件放在complex.h里面。
#include
#include
classcomplex
{
private:
doublere,im;//定义实部和虚部
public:
complex(doubler=1.0,doublei=1.0)
{ re=r;
im=i; }
friendcomplexoperator+(complex&,complex&);
friendcomplexoperator+(double,complex&);
friendcomplexoperator+(complex&,double);
friendvoidoperator++(complex&);
friendcomplexoperator*(complex&,
complex &);
voidprint()
{
cout<<"("<<<")"<}
};
complexoperator+(complex&a,complex&b)
{
returncomplex(a.re+b.re,a.im+b.im);
}
complexoperator+(doublea,complex&b)
{
returncomplex(a.+b.re,a+b.im);
}
complexoperator+(complex&a,doubleb)
{
returncomplex(a.re+b,a.im+b);
}
voidoperator++(complex&a)
{
a.re++;
a.im++;
}
complexoperator*(complex&a,complex&b)
{
returncomplex(a.re*b.re-a.im*b.im,
a.re*b.im+ a.im*b.re);
}
//这里用的公式是:
(A+Bi)*(C+Di)=
//(AC-BD)+(AD+BC)i
说明:
复数类complex定义了两个基本对象,一个作为实部,一个作为虚部。
除了构造函数外,
还定义了五个友员函数,分别用来重载+、++和*,其中前面三个友员函数用来重载+,考虑到实
数+复数和复数+实数两种情况,所以共用了三个友员函数;后面两个重载++和*;print()函数输
出该复数。
主程序放在complex.cpp中:
main()
{
constcomplexnum1(1,0);
//num1=(1.000,0.000)
complexnum2(3.,8.);
//num2=(3.000,8.000)
complexnum3;
//num3=(1.000,1.000)
complexnum4(5,5);
//num4=(5.000,5.000)
doublex=1.0;
doubley=5.0;
num3=num1+num2;
num3.print(); //printsout (4.000,8.000)
num3++;
num3.print(); //printout (5.000,9.000)
complexnum5=num4+x;
num5.print(); //printsout (6.000,6.000)
complexnum6=y+num4;
num6.print();
//printsout (10.000,10.000)
complexnum7=num2*num3;
num7.print();
//printsout (-57.000,67.000)
}
说明:
主程序共声明了7个complex对象,其中num1到num4是采用赋初值的方法赋值的。
num3的
赋值看complex的构造函数,当出现complexnum3这种语句时,构造函数便使re和im等于括号中
赋给的初值,即re=1.0和im=1.0。
当num3=num1+num2之后,num3的值便改变成(1.0,0)+(3.0,8.0),
在num3++之后,num3的实部和虚部就分别加1。
num5和num6是为了实现实数和复数相加。
num7=num2*num3,即num7=(3,8)*(5,9)。
然后将各值分别输出。
注意:
运算符成员函数只能定以运算符的含义,不能改变运算符的优先级和解和顺序;可以定义
多个同名的运算符函数。
但参数类型应有差别,否则会有二义性。
实验思考:
这里只给出了+、++和*的重载,大家可以在下面练习-、--和/的重载,如果有兴趣的
话,还可以练习赋值符=、+=等等的重载。
实验四:
函数的应用
一、实验目的:
掌握函数的定义和调用方法。
练习重载函数使用。
练习函数模板的使用
练习使用系统函数。
二、实验任务:
编写一个函数把华氏温度转换为摄氏温度,转换公式为:
C=(F-32)*5/9。
编写重载函数Max1可分别求取两个整数,三个整数,两个双精度数,三个双精度数的最大值。
使用重函数模板重新实现上小题中的函数Max1。
使用系统函数pow(x,y)计算xy的值,注意包含头文件math.h。
用递归的方法编写函数求Fibonacci级数,观察递归调用的过程。
三、实验步骤
1)编写函数floatConvert(floatTempFer),参数的返回值都为float类型,实现算法C=(F-32)*5/9,在main()函数中实现输入、输出。
程序名为lab3_1.cpp.
2)分别编写四个同名函数max1,实现函数重载,在main()函数中测试函数功能。
程序名:
lab3_2.cpp。
3)使用函数模板实现求任意类型数的最大值,针对不同的参数个数,编写两个同名函数模板max1,其参数个数不同,实现函数重载,在main()函数中测试函数的功能。
和序名:
lab3_3.cpp。
4)在main()函数中提示输入两个整数x,y,使用cin语句得到x,y的值,调用pow(x,y)函数计算x的y次幂的结果,再显示出来。
程序名:
lab3_4.cpp。
5)编写递归函数intfib(intn),在主程序中输入n的值,调用fib函数计算Fibonacci级数。
公式为fib(n)=fib(n-1)+fib(n-2),n>2;fib
(1)=fib
(2)=1。
使用if语句判断函数的出口,在程序中用cout语句输出提示信息。
程序名:
lab3_5.cpp。
6)使用debug中的StepInfo追踪到函数的内部,观察函数的调用过程,参考程序如下:
//lab3_5
#include
intfib(intn);
intmain()
{
intn,answer;
cout<<”Enternumber:
”;
cin>>n;
cout<<”\n\n”;
answer=fib(n);
cout<return0;
}
intfib(intn)
{
if(n<3)return1;
elsereturnfib(n-2)+fib(n-1);
}
7)调试操作步骤如下:
i.选择菜单命令Build|StartDebug|StepIn,系统进入单步执行状态,程序开始运行,并出现一个DOS窗口,此时光标停在main()函数的入口处。
ii.把光标移到语句answer=fib(n)前,从Debug菜单单击RunToCursor,在程序运行的DOS窗口中按提示输入数字10,这时回到可视界面中,光标停在第11行,观察一下n的值。
iii.从Debug菜单中单击StepInto,程序进入fib函数,观察一下n的值,把光标移到语句return(fib(n-2)+