LIAOCHENG UNIVERSITY
面向对象程序设计实验指导书
聊城大学计算机学院
2011年3月
目录
《面向对象程序设计》课程实验教学大纲 (1)
实验一C++对C的扩充 (1)
基本信息 (1)
实验预习 (1)
实验过程 (2)
实验数据和实验结果记录 (8)
实验结果分析 (8)
实验二类和对象 (8)
8
8
9
13
13
13
13
13
14
19
19
19
19
19
20
36
36
37
37
37
37
38
38
《面向对象程序设计》课程实验教学大纲
课程名称:面向对象程序设计
英文名称:Object Oriented Programming
设置形式:非独立设课课程模块:专业核心课实验课性质:专业基础实验课程编号:609324
课程负责人:王玉亭大纲主撰人:王玉亭
大纲审核人:左风朝
一、学时、学分
课程总学时:54 实验学时:16课程学分:3二、适用专业及年级
根据学生实验出勤情况、实验态度、实验报告成绩、实验考核成绩等评定实验成绩。实验报告(含实验理论)占实验成绩的30%,实验技能(含实验态度)占实验成绩的30%,实验考核占实验成绩的40%。最终实验成绩占该课程考核总成绩的20%—30%。
七、实验教科书、参考书
1.实验教科书
自编实验指导书。
2.实验参考书
实验一C++对C的扩充
基本信息
实验课程:面向对象程序设计设课形式:非独立
课程学分:2 实验项目:C++对C的扩充
项目类型:基础项目学时:2
实验预习
并打印分解
米,宽4米
重复进行,小孩数不断减少,圈子也不断缩小。最后所剩的那个小孩就是胜利者。
请找出这个胜利者。
4、编写程序实现五子棋棋游戏。五子棋的规则为:双方各执一色棋子,轮流下
子(将子放在棋盘的任一未下子的点上),直到有一方的棋子有5个排成一线(无论是横、竖还是斜均可),则棋局结束,该方胜利。
实验条件:
1、装有Windows操作系统的微型计算机;
2、Eclipse集成开发环境和CDT插件;
3、MinGW编译环境。
实验设计方案:
1、熟悉在Eclipse集成开发环境下编辑、编译、连接和运行C++程序的方法。
2、借助流程图对程序进行“自顶向下、逐步求精”的结构化分析。
3、熟悉C++中const、引用、new、delete的用法。
4、利用“筛法”生成素数表。
5、实现模拟仿真要利用随机值函数。
实验过程
1、根据实验预习阶段的实验设计方案,编写应用程序。参考代码如下。
#include
#include
const int M = 10001; // 定义验证范围
// 函数CreatPrimeList(): 生成素数表
void CreatPrimeList(int *PrimeList, int n)
{
int i, j;
// 将PrimeList的各元素设置为从0开始的正整数
for (i = 0; i < n; i = i + 1)
{
PrimeList[i] = i;
}
// 分别从表中去掉已经确定的各素数的倍数(将其置为0)
i = 2;
while (i <= sqrt(n))
{
for (j = i + 1; j < n; j = j + 1)
{
if (PrimeList[j] != 0 && PrimeList[j] % PrimeList[i] == 0)
{
PrimeList[j] = 0;
}
}
// 确定下一个素数的位置
i = i + 1;
while (PrimeList[i] == 0)
{
i = i + 1;
}
}
}
// 函数NextPrimeNumber(): 求下一个素数
int NextPrimeNumber(int p, int *PrimeList)
{
p = p + 1;
while (PrimeList[p] == 0)
{
p = p + 1;
}
return PrimeList[p];
}
// 主函数: 在从4到M的范围内验证哥德巴赫猜想
int main()
{
int PrimeList[M]; // 说明存放素数表的数组
int x, p; // 变量x: 偶数, p: 素数
// 建立素数表
CreatPrimeList(PrimeList, M);
// 对从4到M的所有偶数验证哥德巴赫猜想
x = 4;
while (x < M)
{
// 检查偶数减去一个素数后的剩余部分是否仍为素数
p = PrimeList[2];
while (p <= x / 2 && PrimeList[x - p] == 0)
{
p = NextPrimeNumber(p, PrimeList);
}
// 输出检查结果
if (p > x / 2) // 找到了一个不能分解为两个素数和的偶数
{
std::std::cout << "Great discovery: Goldbach is wrong!" << std::endl;
}
else// PrimeList[x-p]≠0, 分解成功
{
std::std::cout << "The even number " << x << "=" << p << " + " << x - p
<< std::endl;
}
// 检查下一个偶数
x = x + 2;
}
return 0;
}
2、根据实验预习阶段的实验设计方案,编写应用程序。参考代码如下。#include
#include
#include
#include
const int SHIP = 1;
const int BAR = 2;
const int WATER = 3;
//一个醉酒者行为的模拟仿真
int drunkard()
{
int x = -10; //记录醉酒者的x坐标,开始时在酒馆门口
int y = 0; //记录醉酒者的y坐标,开始时在跳板的中央
int step = 0; //记录醉酒者一共走了多少步
while (abs(x) <= 10 && abs(y) <= 2)
{
switch (rand() % 10)
{
case 0: //向左走
y = y - 1;
break;
case 1: //向右走
y = y + 1;
break;
case 2: //向后走
x = x - 1;
break;
case 3: //向前走
case 4:
case 5:
case 6:
case 7:
case 8:
case 9:
x = x + 1;
}
step = step + 1;
}
if (x < -10)
{
std::std::cout << "After " << step
<< " steps, the man returned to the bar and drunk again"
<< std::endl;
return BAR;
}
else
{
if (x > 10)
{
std::cout << "After " << step << " steps, the man returned to the ship"
<< std::endl;
return SHIP;
}
else
{
std::cout << "After " << step << " steps, the man dropped into the water"
<< std::endl;
return WATER;
}
}
}
//反映若干个醉酒者最终行为的模拟仿真的主函数
int main()
{
srand(time(0)); //初始化随机种子
int drunkardnumber; //醉酒者总数
int shipnumber = 0; //到达船上的人数
int barnumber = 0; //返回酒馆的人数
int waternumber = 0; //掉进水中的人数
std::cout << "Please input the number of drunkard" << std::endl;
std::cin >> drunkardnumber;
for (int i = 0; i < drunkardnumber; i = i + 1)
{
switch (drunkard())
{
case SHIP:
shipnumber = shipnumber + 1;
break;
case BAR:
barnumber = barnumber + 1;
break;
case WATER:
waternumber = waternumber + 1;
break;
}
}
std::cout << "******************************" << std::endl;
std::cout << "Of all the " << drunkardnumber << " drunkards:" << std::endl;
std::cout << shipnumber << " returned to the ship" << std::endl;
std::cout << barnumber << " went to the bar and drunk again" << std::endl;
std::cout << waternumber << " dropped into the water" << std::endl;
return 0;
}
3、根据实验预习阶段的实验设计方案,编写应用程序。参考代码如下。#include
int main()
{
const int Total = 7; //小孩总数,可以改动。
int ChooseNum; //用户随机选取的数
int boy[Total]; //表示小孩的数组
for (int i = 0; i < Total; i++)
boy[i] = i + 1; //给小孩编号
}
std::cout << " Please input the number which is to be eliminated: ";
std::cin >> ChooseNum; //用户随机输入一个剔除的数
std::cout << " Before eliminating, the boys are:" << std::endl;
for (i = 0; i < Total; i++)
{
std::cout << boy[i] << "\t";
}
std::cout << std::endl;
int k = 1; //第k个离开的小孩
int n = -1; //数组下标,下一个为0表示从第一个孩子开始数数
while (true)
{
//在圈中开始剔除
for (int j = 0; j < ChooseNum;)
{
n = (n + 1) % Total;
if (boy[n] != 0) //如果该小孩还在圈中,则参加计数
{
j++;
}
}
if (k == Total) //如果已经全部剔除完成,则跳出循环
{
break;
}
boy[n] = 0;
std::cout << "After " << k << " times elimination, the boys left are:"
<< std::endl;
for (i = 0; i < Total; i++)
{
if (boy[i] != 0)
{
std::cout << boy[i] << "\t";
}
}
std::cout << std::endl;
k++;
}
// break语句跳转至此,输出胜利者编号
std::cout << "The No." << boy[n] << " boy is the winner." << std::endl;
return 0;
}
4、根据实验预习阶段的实验设计方案,编写应用程序。参考代码如下。#include
#include
const int M = 7; //规定棋盘大小
const int N = 5; //玩几子棋
void InitChess(char*);
void HumanMove(char*);
void ComputerMove(char*);
void ShowChess(char*);
char WinCheck(char*);
int main()
{
char *chess = new char[M * M];if( NULL == chess )
{
return 0;
}
char done;
std::cout<<"---Game of Chess---"< std::cout<<"Human against the stupid computer"< done ='*'; InitChess(chess); do ShowChess(chess); HumanMove(chess); done = WinCheck(chess); //检查是否有赢家 if(done!= '*') { break; //如有赢家,则跳出循环 } ComputerMove(chess); done = WinCheck(chess); //检查是否有赢家}while(done== '*'); ShowChess(chess); //显示棋局最终状态 if(done=='H') { std::cout<<"Human won!"< } else { std::cout<<"Computer won!!!!"< } delete[] chess; return 0; } //初始化棋盘 void InitChess(char *chess) { for (int i = 0; i < M; i++) { for (int j = 0; j < M; j++) { chess[i + M * j] = '*'; } } } //下棋人的落子 void HumanMove(char *chess) { int x, y; std::cout << "Enter X,Y coordinates for your move: "; std::cin >> x >> y; std::cout << std::endl; x--; y--; if (chess[x + M * y] != '*') { std::cout << "Invalid move, try again." << std::endl; HumanMove(chess); } else chess[x + M * y] = 'H'; } //计算机落子 void ComputerMove(char *chess) { int i, j; for (i = 0; i < M; i++) { for (j = 0; j < M; j++) { if (chess[i + M * j] == '*') { break; } } if (chess[i + M * j] == '*') { break; } } if (i * j == M * M) { std::cout << "Diamond cut diamond" << std::endl; exit(0); } else { chess[i + M * j] = 'C'; } } //在屏幕上显示棋局 void ShowChess(char *chess) { for (int i = 0; i < M; i++) { for (int j = 0; j < M; j++) { std::cout << chess[i + M * j] << "\t"; } std::cout << std::endl << std::endl; } } //检查是否有赢家 char WinCheck(char *chess) { for (int i = 0; i < M; i++) { for (int j = 0; j < M; j++) { char t = chess[i + M * j]; int count = 1; for (int a = i + 1, b = j + 1; chess[a + M * b] == t && a < M && b < M; a++, b++) { count++; } if (count >= N) { return t; } count = 1; for (a = i, b = j + 1; chess[a + M * b] == t && b < M; b++) { count++; } if (count >= N) { return t; } count = 1; for (a = i + 1, b = j; chess[a + M * b] == t && a < M; a++) { count++; } if (count >= N) { return t; } count = 1; for (a = i + 1, b = j - 1; chess[a + M * b] == t && a < M && b >= 0; a++, b--) { count++; } if (count >= N) { return t; } } } return'*'; } 实验数据和实验结果记录 根据程序运行情况如实记录实验结果。 实验结果分析 1、分析生成素数表的“筛法”。 2、分析机构化程序设计方法。 3、写出自己的心得体会。 实验二类和对象 基本信息 实验课程:面向对象程序设计设课形式:非独立 课程学分:3 实验项目:类和对象 项目类型:基础项目学时:4 实验预习 实验目的和要求: 1、掌握声明类的方法,类和类的成员的概念以及定义对象的方法。 2、初步掌握用类和对象编制基于对象的程序。 3、学习检查和调试基于对象的程序。 4、掌握类的构造函数和析构函数的概念和使用方法。 5、掌握对象数组、对象的指针及其使用方法。 实验内容和原理或涉及的知识点: 1、用类实现顺序栈。 2、用类实现循环队列栈。 3、实现一个带有头结点的单链表,该链表可以插入整型元素。 实验条件: 1、装有Windows操作系统的微型计算机; 2、Eclipse集成开发环境和CDT插件; 3、MinGW编译环境。 实验设计方案: 1、熟悉C++程序中源文件的组织方式。 2、首先分析类的属性和行为,然后通过封装实现类。 3、实现栈时,考虑栈容量的自动增长问题。 实验过程 1、根据实验预习阶段的实验设计方案,编写应用程序。参考代码如下。 //Stack.h的内容 #ifndef STACK_H_ #define STACK_H_ class Stack { int *m_Elem; int m_nTop; const int m_nSize; public: Stack(int size) : m_nTop(0), m_nSize(size) { m_Elem = new int[size]; } ~Stack() { delete[] m_Elem; } bool IsEmpty() { return m_nTop == 0; } bool IsFull() { return m_nTop == m_nSize; } bool Push(int e); bool Pop(int &e); }; #endif/* STACK_H_ */ //Stack.cpp的内容 #include"Stack.h" bool Stack::Push(int e) { if (IsFull()) { return false; } else { m_Elem[m_nTop] = e; ++m_nTop; return true; } } bool Stack::Pop(int &e) { if (IsEmpty()) { return false; } else { --m_nTop; e = m_Elem[m_nTop]; return true; } } 2、根据实验预习阶段的实验设计方案,编写应用程序。参考代码如下。//Queue.h的内容 #ifndef QUEUE_H_ #define QUEUE_H_ class Queue { int front, rear int *data; const int size; public: Queue(int s) : front(0), rear(0), size(s) { data = new int[size + 1]; } bool IsEmpty() { return rear == front; } bool IsFull() { return (rear+1)%(size+1) == front; } bool EnQueue( int v ); bool DeQueue( int &v ); ~Queue() { delete[] data; } }; #endif/* QUEUE_H_ */ //Queue.cpp的内容 #include"Queue.h" bool Queue::EnQueue(int v) { if (IsFull() == true) { return false; } else { data[rear] = v; rear = (rear + 1) % (size + 1); return true; } } bool Queue::DeQueue(int &v) { if (IsEmpty() == true) { return false; } else { v = data[front]; front = (front + 1) % (size + 1); return true; } } 3、根据实验预习阶段的实验设计方案,编写应用程序。参考代码如下。//list.h的内容 #ifndef LIST_H_ #define LIST_H_ #include #include class List//定义整型链表类 { private: class Node { public: Node(const int &data = 0, Node *next = NULL) : m_nData(data), m_pNext(next) { } Node *m_pNext; //指向下一个结点的指针成员 int m_nData; //指向本结点数据的指针成员 }; public: List(); //构造函数 ~List(); //析构函数 bool Append(const int &e, int index = 0); //在inedex位置后增加节点bool Prepend(const int &e, int index = 1); //在inedex位置前增加节点bool Remove(int &e, int index = 1); //删除inedex位置处的节点 bool Find(int &e, int index = 1); //查找inedex位置处的节点 void PrintList(); //打印链表 protected: Node *m_pFirst; //头节点 }; #endif/* LIST_H_ */ //List.cpp的内容 #include"List.h" List::List() //构造函数 { m_pFirst = new Node(); } List::~List() //构造函数 { Node * p = m_pFirst; Node * q; while (NULL != p) //寻找inedex位置前一个节点 { q = p; p = p->m_pNext; delete q; } } bool List::Append(const int &e, int index) //在inedex位置后增加节点{ if (index < 0) { return false; } Node *p = m_pFirst; int i = 0; while (NULL != p && i < index) //寻找inedex位置处节点 { ++i; p = p->m_pNext; } if (NULL == p) { return false; } Node *q = new Node(e, p->m_pNext); p->m_pNext = q; return true; } bool List::Prepend(const int &e, int index) //在inedex位置前增加节点{ if (index < 1) { return false; } Node *p = m_pFirst; int i = 0; while (NULL != p && i < index - 1) //寻找inedex位置前一个节点{ ++i; p = p->m_pNext; } if (NULL == p) { return false; } Node *q = new Node(e, p->m_pNext); p->m_pNext = q; return true; } bool List::Remove(int &e, int index) //删除inedex位置处的节点 { if (index < 1) { return false; } Node *p = m_pFirst; int i = 0; while (NULL != p && i < index - 1) //寻找inedex位置前一个节点{ ++i; p = p->m_pNext; } if (p == NULL || NULL == p->m_pNext) { return false; } Node *q = p->m_pNext; p->m_pNext = q->m_pNext; e = q->m_nData; delete q; return true; } bool List::Find(int &e, int index) //查找inedex位置处的节点 { if (index < 1) { return false; } Node *p = m_pFirst; int i = 0; while (NULL != p && i < index) //寻找inedex位置处节点 { ++i; p = p->m_pNext; } if (NULL == p) { return false; } e = p->m_nData; return true; } void List::PrintList() //打印链表 { Node *p = m_pFirst->m_pNext; while (NULL != p) //寻找inedex位置处节点 { std::cout << p->m_nData << '\t'; p = p->m_pNext; } std::cout << std::endl; } //main.cpp的内容 int main() { List l; l.Append(1); l.Prepend(10); int s; l.Find(s, 2); l.Remove(s, 2); l.PrintList(); return 0; } 实验数据和实验结果记录 根据程序运行情况如实记录实验结果。 实验结果分析 1、分析C++程序原文件的组织方式。 2、分析new和delete的用法。 3、对抽象技术和封装技术进行分析。 4、写出自己的心得体会。 实验三继承和组合 基本信息 实验课程:面向对象程序设计设课形式:非独立 课程学分:3 实验项目:继承和组合项目类型:基础项目学时:4 实验预习 实验目的和要求: 1、了解继承在面向对象程序设计中的重要作用。 2、进一步理解继承与派生的概念。 3、掌握通过继承派生出一个新的类的方法。 4、了解虚基类的作用和用法。 5、掌握类的组合。 实验内容和原理或涉及的知识点: 1、上机分析实例程序,理解继承的概念。 2、上机分析实例程序,理解继承下构造函数和析构函数的执行顺序。 3、声明一个Shape(形状)基类,它有两个派生类:Circle(圆)和Square (正方形)。 要求:(1)根据给出的圆心坐标和半径计算圆的面积;(2)根据给出的正方形中点坐标和一个顶点坐标计算正方形的面积。提示:Shape类的数据成员包括中心点的坐标,Circle类中新增一个数据成员,即圆的半径,Square类新增一个顶点的坐标。 4、编程实现单件模式,理解静态成员。 5、某出版系统发行图书和磁带,利用继承设计管理出版物的类。要求如下:建 立一个基类Publication存储出版物的标题title、出版物名称name、单价price 及出版日期date。用Book类和Tape类分别管理图书和磁带,它们都从Publication 类派生。Book类具有保存图书页数的数据成员page,Tape类具有保存播放时间的数据成员playtime。每个类都有构造函数、析构函数,且都有用于从键盘获取数据的成员函数inputData(),用于显示数据的成员函数display()。 实验条件: 1、装有Windows操作系统的微型计算机; 2、Eclipse集成开发环境和CDT插件; 3、MinGW编译环境。 实验设计方案: 1、进一步熟悉C++程序中源文件的组织方式。 2、首先分析类之间的继承组合,然后实现类。 实验过程 1、上机分析下面程序,理解继承的概念。 //头文件person.h的内容 #include #include class Person { private: char m_strName[20]; int m_nAge; int m_nSex; public: Person(); //构造函数 Person(const char *name, int age, char sex); //构造函数 Person(const Person &p); //拷贝构造函数 ~Person() //析构函数 { std::cout << "Now destroying the instance of Person" << std::endl; } void SetName(const char *name); void SetAge(int age); void setSex(char sex); const char* GetName() const; int GetAge() const; char GetSex() const; void ShowMe() const; }; //源程序文件person.cpp的内容: #include"person.h" Person::Person() : m_nAge(0), m_nSex(0) //构造函数 { strcpy(m_strName, "XXX"); } Person::Person(const char *name, int age, char sex) : m_nAge(age), m_nSex(sex == 'm' ? 0 : 1) //构造函数 { strcpy(m_strName, name); } Person::Person(const Person &p) : m_nAge(p.m_nAge), m_nSex(p.m_nSex) //拷贝构造函数 { strcpy(m_strName, p.m_strName); } void Person::SetName(const char *name) { strcpy(m_strName, name); } void Person::SetAge(int age) { m_nAge = age; } void Person::setSex(char sex) { m_nSex = sex == 'm' ? 0 : 1; } const char* Person::GetName() const { return m_strName; } int Person::GetAge() const { return m_nAge; } char Person::GetSex() const { return (m_nSex == 0 ? 'm' : 'f'); } void Person::ShowMe() const { std::cout << GetName() << '\t' << GetAge() << '\t' << GetSex() << '\t'; } //头文件employee.h的内容: #include"person.h" class Employee: public Person//雇员类定义 { char m_strDept[20]; //工作部门 float m_fSalary; //月薪 public: Employee(); 《面向对象程序设计》课程实验指导书 Employee(const char *name, int age, char sex, const char *dept, float salary); Employee(const Employee &e); ~Employee() { std::cout << "Now destroying the instance of Employee" << std::endl; } void SetDept(const char *dept); void SetSalary(float salary); const char* GetDept() const; float GetSalary() const; void ShowMe() const; //显示雇员信息 }; //源程序文件employee.cpp的内容: #include"employee.h" Employee::Employee() : m_fSalary(0.0) { strcpy(m_strDept, "xxxx"); } Employee::Employee(const char *name, int age, char sex, const char *dept, float salary) : Person(name, age, sex), m_fSalary(salary) { strcpy(m_strDept, dept); } Employee::Employee(const Employee &e) : Person(e.GetName(), e.GetAge(), e.GetSex()), m_fSalary(e.m_fSalary) { strcpy(m_strDept, e.m_strDept); } void Employee::SetDept(const char *dept) { strcpy(m_strDept, dept); } void Employee::SetSalary(float salary) { m_fSalary = salary; } const char* Employee::GetDept() const { return m_strDept; } float Employee::GetSalary() const { return m_fSalary; } void Employee::ShowMe() const { Person::ShowMe(); std::cout << m_strDept << "\t" << m_fSalary << std::endl; } //源程序文件main.cpp的内容: #include"employee.h" int main() { Employee emp1; emp1.ShowMe(); Employee emp2("张莉", 40, 'f', "图书馆", 2000); emp2.ShowMe(); std::cout << "调用基类GetName()返回值为: " << emp2.GetName() << std::endl; return 0; } 2、上机分析下面程序,理解继承下构造函数和析构函数的执行顺序。#include