文档发表日期:2011-04-14
最近修改日期:2011-05-03
《面向对象程序设计》实验任务及指导
目录
一、实验1:熟悉实验环境
二、实验2:数据抽象
三、实验3:继承与多态性
三、实验4:重载和I/O流及面向对象程序设计
一、实验1:熟悉实验环境
【实验目的】
●熟悉Visual C++6.0开发环境。
●掌握多文件结构程序项目的建立过程。
●初步掌握面向对象程序的多文件结构项目的开发运行步骤。
【实验内容】
1、学习多文件结构程序项目的建立过程,观看、体会演示。
2、下面的程序在编译时会出错,请修改程序,使之能正确运行。
#include
using namespace std;
class MyClass
{
public:
MyClass() { x=0;y=0; }
MyClass(int a = 0, b = 0);
Print();
private:
int x, y;
};
MyClass::MyClass(int a = 0, int b =0)
{
x = a;
y = b;
}
void MyClass::Print()
{
cout << "x = " << x << endl;
cout << "y = " << y << endl;
}
int main()
{
MyClass obj1,obj2(5,8);
obj1.x =1;
obj1.y =3;
obj1.Print();
obj2.Print();
return 0;
}
3、将任务2得到的正确程序分成3个文件。文件MyClass.h包括类的定义,文件
MyClass.cpp包括类成员函数的实现,文件App.cpp包括类的使用。调试、运行该程序,使之获得正确结果。
【实验指导】
1、头文件中通常包含常量、类型、函数原型的定义。类是用户定义的类型,故将类的
定义放在头文件中。函数的定义即函数的实现应放在实现文件.cpp文件中。这样的安排体现了信息隐藏和模块化。将类成员函数的实现和类的使用放在不同的cpp文件中,可将类的实现与类的使用分离,体现了对接口编程的思想。
2、文件MyClass.h中的结构
#include
using namespace std;
// MyClass的定义
文件MyClass.cpp中的结构
#include “MyClass.h”
// MyClass成员函数的定义
文件App.cpp中的结构
#include “MyClass.h”
// main函数的定义
3、避免头文件内容被重复引入。头文件内容被重复引入会导致标识符重复定义的错误。
用预编译条件指令来限制头文件内容的引入。例如:
#ifndef MYCLASS
#define MYCLASS
// 头文件的内容
#endif
二、实验2:数据抽象
【实验目的】
●掌握类的定义和使用以及类对象的声明和使用方法,理解具有不同访问属性的成员
的访问方式。
●掌握构造函数和析构函数的作用和编写方法。
●了解并掌握静态成员、常成员的使用。
【实验内容】
1、下面程序中定义了一个整型动态数组,编写了3个构造函数。
#include
using namespace std;
class IntArray {
public:
IntArray(int sz); // 数组初始化为sz个元素,初值全为0
IntArray( int *array, int sz ) ; // 用静态数组array的前sz个元素初始化新数组IntArray( const IntArray &rhs ) ; // 拷贝构造函数
void printAll();
private:
int *ia;
int size;
};
IntArray::IntArray( int sz ) {
size = sz; // 设置数据成员
ia = new int[ size ];
for ( int ix=0; ix < size; ++ix )
ia[ix] = 0; // 初始化数组元素
}
IntArray:: IntArray( int *array, int sz ) {
size = sz; // 设置数据成员
ia = new int[ size ];
for ( int ix=0; ix < size; ++ix )
ia[ix ] = array[ ix ]; // 拷贝数据成员
}
IntArray:: IntArray( const IntArray &rhs )
{
size = rhs.size;
ia = new int[ size ];
for (int ix = 0; ix < size; ix++ )
ia[ ix ] = rhs.ia[ ix ];
}
void IntArray::printAll()
{
for ( int ix=0; ix < size; ++ix )
cout< cout< } int main() { int a[10]={1,2,3,4,5,6,7,8,9,10}; IntArray arr1(10), arr2(a,5),arr3(arr2); arr1.printAll(); arr2.printAll(); arr3.printAll(); return 0; } (1)为IntArray加上析构函数并编译运行程序,体会类的构造函数和析构函数的作用。 (2)程序中,类IntArray的三个构造函数都是以相似的方式来实现的。一般来说,当两个或多个函数重复相同的代码时,可将这部分代码抽取出来,形成独立的函数,以便共享。以后如果需要改变这些实现,则只需改变一次,而且这种实现的共享本质更容易为大家所理解。怎样把构造函数中的代码抽取出来形成独立的共享函数呢?请给出一种可能的实现。按你给出的实现方法修改程序并重新编译运行。 2、设有一个点myPoint类的定义如下: class myPoint { public: myPoint(double x0=0.0,double y0=0.0):x(x0),y(y0) {} myPoint(myPoint &np):x(np.x),y(np.y) {} double GetX() { return x;} double GetY() {return y;} void SetX(double x0) {x=x0;} void SetY(double y0) {x=y0;} void SetPoint(double x0,double y0) {x=x0;y=y0;} void SetPoint(myPoint &np) { x=np.x; y=np.y;} double GetLength(myPoint p) { return sqrt((x-p.x)*(x-p.x) +(y-p.y)*(y-p.y)); } void Printit() { cout<<" ("< private: double x ,y; }; 试定义一个三角形Triangle类,在Triangle类中以点myPoint类的3个对象p1、p2、p3作为数据成员,表示三角形的三个顶点。Triangle类具有计算三角形的周长和面积的功能。请编写程序上机调试并运行。 【实验指导】 1、(1)可参考教材第192页关于“浅复制”和“深复制”的示例来实现析构函数的定 义。(2)在类中将构造函数的代码抽取出来形成独立的函数,然后在构造函数中调用执行,注意函数参数的设置和定义。 2、由于在Triangle类中需要定义myPoint类的对象,因此需要在Triangle类的构造方 法的成员初始化列表中实现对成员对象的初始化。 三、实验3:继承与多态性 【实验目的】 ●掌握继承的概念,能够定义和使用类的继承关系。 ●了解在派生类中如何使用基类的成员以及基类成员在派生类中的访问控制特性。 ●理解虚基类在解决二义性问题中的作用。 ●理解静态多态性和动态动态性的含义。掌握使用虚函数和继承实现动态多态性的方 法。 【实验内容】 1、有如下的程序: #include using namespace std; class Base { public : Base(int p1, int p2) { data1 = p1; data2 = p2; } int Inc1( ) { return ++data1; } int Inc2( ) { return ++data2; } void Display() { cout<<"data1 = "<< data1 << " data2 = " << data2 << endl; } protected: int data1, data2; }; class D1 : public Base { public : D1(int p1, int p2, int p3) : Base(p1, p2) { data3 = p3; } int Inc1( ) { return Base::Inc1( ); } int Inc3( ) { return ++data3; } void Display( ) { cout<<"data1 = "<< data1 << " data2 = " << data2 << " data3 = " << data3 < cout<<"Base::Display() ----"; Base::Display( ); } protected: int data3; }; class D2 : public Base { public : D2(int p1, int p2, int p4) : Base(p1, p2) { data4 = p4; } int Inc1() { Base::Inc1( ); Base::Inc2( ); return Base::Inc1( ); } int Inc4( ) { return ++data4; } void Display( ) { cout<<"data1 = "<< data1 << " data2 = " << data2 << " data4 = " << data4 < cout<<"Base::Display() ----"; Base::Display( ); } protected: int data4; }; class D12 : public D1, public D2 { public : D12(int p11,int p12,int p13,int p21,int p22,int p23,int p) : D1(p11, p12, p13), D2(p21, p22, p23) { data5 = p; } int Inc1( ) { D1::Inc1(); D2::Inc1( ); return D1::Inc1( ); } int Inc5( ) { return ++data5; } void Display( ) { cout<<"data1 = "<< data1 << " data2 = " << data2 < cout <<" data3 = " << data3 <<"data4 = "<< data4 << " data5 = " << data5 < cout << "D1::Display( )----"; D1::Display( ); cout << "D2::Display( )----"; D2::Display( ); } private: int data5; }; int main() { D12 d(1, 2, 3, 4, 5, 6, 7); d. Display( ); cout << endl; d.Inc1(); d.Inc2(); // ② d.Inc3(); d.Inc4(); d.Inc5(); d.D12::Inc1(); d.Display(); return 0; } (1)这个程序在编译时会出现错误,请根据出错提示信息找出出错的原因。 (2)修改程序中的错误,使之能正确运行。 2、设有一个点类Point的定义如下: Point { public: Point() {x = 0; y = 0; } Point(double xv,double yv) {x = xv;y = yv;} Point(Point& pt) { x = pt.x; y = pt.y; } double getx() { return x; } double gety() { return y; } double Area() { return 0; } void Show() { cout<<"x="< private: double x,y; }; 编写程序,以点point类为基类,派生出矩形类Rectangle和圆类Circle。矩形由左上角的顶点和长、宽定义。圆由圆心和半径定义。派生类中新增的成员函数position(Point &pt)用于判断任一坐标点是在图形内、还是在图形的边缘上,还是在图形外。 3、设有几何图形的派生关系如下图14-2所示。 几何图形 geometric_shape) 矩形 rectangle 圆 circle 三角形(triangle) 三棱锥(t_pyramid) 长方体 box)圆柱 cylinder)圆锥 cone)三棱柱(t_prism) 图14-2 几何图形中类的继承关系 对平面图形可求周长和面积,对立体图形可以求体积以及底面图形的周长和底面积。 设有主函数如下: int main() { Geometric_shape * gs[]={new Circle(10),new Rectangle(6,8),new Triangle(3,4,5), new Box(6,8,3),new Cylinder(10,3),new Cone(10,3),new T_pyramid(3,4,5,3),new T_prism(3,4,5,3)}; for (int i=0;i<8;i++) { gs[i]->Show(); cout< } for (i=0;i<8;i++) { gs[i]->Show(); cout< } cout<<"平面图形:"< for (i=0;i<3;i++) { cout<<"图形周长:"< cout<<"图形面积:"< cout<<"图形体积:"< } cout<<"立体图形:"< for (i=3;i<8;i++) { cout<<"图形底周长:"< cout<<"图形底面积:"< cout<<"图形体积:"< } return 0; } 请编写各类的定义和实现代码,使给定的主函数main可以正确运行。 【实验指导】 1、在派生类中对基类成员的访问应该是唯一的。但是,在有多重继承的情况下,可能 会造成派生类对基类中某个成员的访问出现不唯一的情况,这时就称对基类成员的访问产生了二义性。C++为此提供了虚基类,以解决这种二义性问题。 2、由于基类的构造函数不能够被派生类继承。因此,派生类的构造函数必须通过调用 基类的构造函数来初始化基类数据成员。所以在定义矩形类Rectangle和圆类Circle的构造函数时,除了对新增数据成员进行初始化,还必须负责调用基类的构造函数使基类数据成员得以初始化。同时,在矩形类Rectangle和圆类Circle还需分别提供计算面积、周长等函数,以满足实际应用的需求。 3、用虚函数来实现主程序中的动态联编。即在Geometric_shape类中分别将计算面积、 周长、体积等函数声明为虚函数后,就可以在该类的(直接或间接)派生类中定义与其基类虚函数原型相同的函数。这时,当用基类指针指向这些派生类的对象时,系统会自动用派生类中的同名函数来代替基类中的虚函数,从而实现运行时的多态。 四、实验4:重载和I/O流及面向对象程序设计 【实验目的】 ●掌握运算符重载的基本方法。 ●掌握标准输入输出的使用及格式控制方法。 ●掌握磁盘文件(如二进制文件、文本文件)的输入输出的方法。 【实验内容】 1、编写程序,重载运算符“<<”和“>>”,使用户能直接输入和输出固定电话的号码。电话号码以如下形式输入和输出:(027)xxxxxxxx 2、编写一个程序,实现以下功能: (1)输入一系列的学生成绩(包括学号、姓名、成绩等数据)存放在文件stud.dat中。 (2)从stud.dat文件中读出这些数据并显示出来。 (3)在stud.dat文件中按姓名进行查询,如输入“李”,则将所有姓李的学生的数据都显示出来。 所编写的程序运行结果示例如下: 选择(1:输入数据2:输出数据3:按姓名查找数据其他退出):1 输入数据 学生人数:5 第1个学生(学号姓名成绩):1001 张三89 第2个学生(学号姓名成绩):1002 李四78 第3个学生(学号姓名成绩):1003 王五92 第4个学生(学号姓名成绩):1004 李沅芷88 第5个学生(学号姓名成绩):1005 赵六56 选择(1:输入数据2:输出数据3:按姓名查找数据其他退出):2 输出数据 学号姓名成绩 1001 张三89 1002 李四78 1003 王五92 1004 李沅芷88 1005 赵六56 选择(1:输入数据2:输出数据3:按姓名查找数据其他退出):3 输入姓名:李 输出匹配的结果: 学号姓名成绩 1002 李四78 1004 李沅芷88 选择(1:输入数据2:输出数据3:按姓名查找数据其他退出):3 输入姓名:李四 输出匹配的结果: 学号姓名成绩 1002 李四78 选择(1:输入数据2:输出数据3:按姓名查找数据其他退出):0 【实验指导】 1、在重载运算符“<<”和“>>”时,不能将其定义为类的成员函数,只能定义为类的 友元函数。 2、在进行输入输出时,程序中如果使用带参数的操纵符,必须使用预编译命令 #include 将需要的头文件包含进来。