VC++6.0面向对象程序设计上机指导书
掌握VC上机的过程
在读者已掌握Windows 操作系统基础上,学会用Visual C++完整的完成一个程序从输入到运行输出结果的全过程。
Visual C++ 6.0 是MicroSoft公司开发的基于C/C++的集成工具,是面向对象的、可视化的软件开发环境。主要用于编写在Windows操作系统上运行的应用程序。包括编辑器、编译器、调试器、连接器和资源管理器等。另外,还提供了许多向导。用户可利用VC以两种方式编写Windows应用程序,一种是基于Windows API的C编程方式,另一种是基于MFC的C++编程方式。C编程方式是传统的、久经考验的编程方式,代码效率较高,但开发难度与开发工作量大。C++编程方式代码运行效率相对较低,但开发难度小,开发工作量小。在C++编程方式中,主要以MFC提供的许多类库,包括对话框、文档/视图结构、图形处理、鼠标消息处理、颜色的使用、滚动处理、文件读写、文档打印和多视图等作为基础。
要学习用MFC的编程,首先要学习面向对象的程序设计的基本知识。因此结合我们的课程,对习题用VC来编写面向对象的程序,并在VC上编译运行。这对以后学习用MFC编程很有好处。在这里为了直观、方便,我们仍在MS-DOS环境下运行可执行程序,但这并不影响以后进一步学习MFC。
Visual C++ 的上机过程
安装好Visual C++ 后,在Windows的“程序”菜单中有如图1 菜单。
图 1
鼠标双击MicroSoft Visual C++ 5.0(6.0)后 ,得到的屏幕:图2
图 2
1. 编辑源程序和错误修改及文件存盘
当你在纸上已编好了C++的源程序,想输入源程序时,称为编辑源程序的操作,可点击以上的屏幕中的File菜单中的New菜单:图3,得到如下的屏幕:图4
图 3
图 4
选中Files卡片,并选C++ Source File菜单,点击 OK: 图5
图 5
得到如下的屏幕图6,即可在光标处开始输入源程序。
在输入的过程中,完全可以用Microsoft Word的方法对文字进行插入、删除、修改、复制、粘贴、移动等操作。图7
当程序输入完毕,可用File菜单中的Save 菜单,把程序存到你的盘和文件夹(保存在:)及你所取的文件名(文件名:)中:图8
以后,想再把该程序调入时,可用File菜单中的Open菜单,在你的盘和文件夹(Directories)中,选中文件名(Files),单击“打开”即可打开。
图 6
图 7
注意:取文件名时,名字中间不能有点,如取成“mao1.1”是错误的,到时编译将会出错,要取成“mao1_1”(用下划线),后面加 “.cpp”成为“mao1_1.cpp”。 “.cpp”是C++源程序文件专用的扩展名。
图 8
2.编译源程序和出错调试
当程序输入和程序存盘以后,可选Build菜单Compile进行编译源程序:
单击 Compile菜单系统进行编译,若有错误会显示出错信息(Message),错误分严重性错误(Error)和警告性错误(Warning)图9。如下面程序的第6行前缺少“;”号(Statement missing ;Function call missing )),第9行缺少“)”号(Function call missing ))。
图 9
移动右边的滑块,可显示所有的出错信息。把光标对准行号如(6)图10,双击鼠标,会自动指向出错的源程序行。在出错的源程序行中仔细地查找出错的原因。VC++有各种各样的编译出错信息,要学会看懂它(英文的),可参看“帮助”,或请教指导老师。指导老师自己要多作编程的训练,增强查错经验。
图 10
回到编辑状态,修改源程序,重新编译,直到没有错误为止:图11
图 11
3.连接目标程序和出错调试
当编译没有错误或只有个别的警告性错误(如float x; x=3.4;这时会出现警告性错误,因为x是单精度的浮点数,而常数3.4默认为双精度的浮点数,赋值时会出现警告,这种警告性错误可以不管它,如要不警告,可改为x=3.4f),就可以进行连接目标程序,产生可执行程序。可选Build菜单的Build菜单:图12,单击Build菜单系统进行连接目标程序,产生可执行程序,若有错误会显示出错信息(Message),错误分严重错误(Error)和警告性错误。
图 12
4. 运行可执行程序和出错调试
当连接成功后,可运行可执行程序:图13选菜单!Execute mao2_1.exe 。
进入运行屏幕,输入数据,得出运行结果:图14 。
当运行出错时,最主要的原因是程序的逻辑错。这时要仔细的检查源程序进行修改。只要改动一点点,就要重新进行编译和连接。另外,在运行时,如数据的格式输入出错也会引起错误,如上例中应该用逗号分开两个整数,而用空格分开时就会引起错误。有的程序在scanf中少了地址符&,编译和连接是不作为错误的,运行时也就出错了。
图 13
图 14
输入数据123 456 ,得出运行结果:图15
图 15
敲任意键,回到源程序状态。
二、不退出VC再编一个C++源程序
不退出VC再编一个C++源程序,有时在连接时会出错。如再编一个叫mvc2_2.cpp的C++源程序如 图16
图 16
该源程序编译通过没有错误,当连接时会出显如图17的情况:
图 17
其中,Compile mvc2_2.cpp 表示编译的是mvc2_2.cpp源程序。而Build mvc2_1.exe 表示连接的还是前一个程序,二者不一致。这时连接出错。要解决这个问题,可击图18鼠标所指的打开工作空间(Working space)
图 18
的按钮,打开工作空间窗口,出现如图19,单击鼠标所指的Fil...按钮
图 19
单击出现如图20的mvc2_1 files
图 20
单击Edit菜单,如图21
图 21
单击Delete菜单,删除mvc2_1 files。
关闭工作空间窗口,重新编译,再连接,就不会再出错。图 22
图 22
VC++6.0面向对象程序设计实验提纲
按课程学习的进程安排上机内容,由浅及深把课程习题通过上机解决。
一. 熟悉C++程序的风格和C++程序的上机全过程
【实验目的】
1. 熟悉C++程序的风格。
2. 练习C++程序的输入、修改、编译、连接与运行的全过程。
【实验内容】
1. 习题2.1下面是一个C程序,改写它,使它采用C++风格的I/O语句。
#include <iostream.h>
int main()
{ int a,b,d,min;
cout<<"输入两个整数:";
cin>>a;
cin>>b;
min=a>b?b:a;
for(d=2;d<min;d++)
if((a%d==0)&&(b%d==0))break;
if(d==min)
{ cout<<"没有公约数!\n";
return 0;
}
cout<<"最小公约数是:"<<d<<endl;
return 0;
}
2. 习题 2.7分析下面程序的输出结果:
#include <iostream.h>
int &f(int &i)
{ i+=10;
return i;
}
void main()
{ int k=0;
int &m=f(k);
cout<<k<<endl;
m=20;
cout<<k<<endl;
}
3. 习题2.8 建立一个被称为sroot( )的函数,返回其参数的二次根。重载sroot( )三次,让它分别返回整数、长整数与双精度数的二次根(为了实际计算二次根,可以使用标准库函数sqrt( ))。
#include <iostream.h>
#include <math.h>
int sroot(int i)
{ return (int)sqrt(i); }
float sroot(float f)
{ return (float)sqrt(f); }
double sroot(double d)
{ return (double)sqrt(d); }
void main( )
{ int i=12;
float f=3.4;
double d=5.67;
cout<<"i 的二次根="<<sroot(i)<<endl;
cout<<"f 的二次根="<<sroot(f)<<endl;
cout<<"d 的二次根="<<sroot(d)<<endl;
}
4. 习题2.9编写一个程序,用C++风格的I/O从键盘输入两个整数,然后显示以第一个数为底,第二次数为指数的结果(例如,用户输入2与4,那么结果就是16)。
#include <iostream.h>
int main()
{ int a,b,r=1;
cout<<"输入两个整数:";
cin>>a;
cin>>b;
for(int i=1;i<=b;i++)
r=r*a;
cout<<a<<"的"<<b<<"次方是:"<<r<<endl;
return 0;
}
二. 熟悉类和对象的定义和使用
【实验目的】
1. 熟悉C++程序类定义的一般形式。
2. 熟悉C++程序对象的定义。
构造函数与析构函数的定义
4. 对象的使用
【实验内容】
1. 习题3.6下列程序在运行时将产生错误,为什么?如何解决这个问题?
#include <iostream.h>
#include <string.h>
class string {
private:
char *str;
public:
string(char *s)
{ str=new char[strlen(s)+1];
strcpy(str,s); }
void print() { cout<<str<<endl; }
// ~string() { delete str; }
};
void main()
{ string s1="Beijing";
string s2=s1;
s1.print();
s2.print();
}
2. 习题 3.8 定义一个数score,它含有私有数据成员english_score(英语分数)、公有成员函数setscore( )和printscore( ),其中setscore( )用来设置english_score的值,print-score( )用来输出english_score的值。在主程序中定义类score的两个对象stu1和stu2,其英语成绩分别为85.5和93.5 ,输出这两个分数。
#include <iostream.h>
class score {
private:
float english_score;
public:
void setscore(float c) { english_score = c; }
void printscore() { cout<<english_score<<endl; }
};
void main()
{ score stu1, stu2;
stu1.setscore(85.5);
stu2.setscore(93.5);
stu1.printscore();
stu2.printscore();
}
3. 习题 3.9 下面是一个计算机器的定义,请完成该类的实现。
#include <iostream.h>
class counter{
int value;
public:
counter(int number) { value= number; }
void increment() { value++; }
void decrement() { value--; }
int getvalue() { return value; }
void print() { cout <<"计数器的值为 "<<value<<endl; }
};
void main( )
{ counter ct(0);
int top,low;
for (int i=1; i<=100; i++ )
{ ct.increment(); ct.print(); top=ct.getvalue(); }
for (i=1; i<=100; i++ )
{ct.decrement(); ct.print(); low=ct.getvalue(); }
cout << "top="<<top<<",low="<<low<<endl;
}
4. 习题 3.10试定义一个字符串类string,使其至少具有内容(contents)和长度(length)两个数据成员,并具有显示字符串、求字符串长度、给原字符串后添加一个字符串等功能。
#include <iostream.h>
#include <string.h>
class string {
private:
char *content;
int length;
public:
string(char *s)
{ content=new char[strlen(s)+1];
strcpy(content,s);
length=strlen(s); }
void print() { cout<<content<<endl; }
int strlength() { return length; }
void strcating(char *st)
{ strcat(content,st); }
// ~string() { delete content; }
};
void main()
{ string s("Zheijing");
s.print();
s.strlength();
s.strcating("Hong zhei");
s.print();
s.strlength();
}
5. 习题 3.11 建立类box,box的构造函数被传递了三个double值,每一个double值表示盒子的一条边长。用box类计算立方体的体积,并存储在一个double变量中。在box类中包含一个成员函数vol( ),用来显示每个box对象的体积。
#include <iostream.h>
class box {
private:
double x,y,z;
double body;
public:
box(double a, double b, double c)
{ x=a; y=b; z=c; body=x*y*z; }
void vol() { cout << "该立方体的体积是: "<<body<<endl; }
};
void main( )
{ box a(10,20,3);
a.vol();
}
三. 熟悉类的派生与继承
【实验目的】
1. 熟悉派生类的声明。
2. 派生类的构造函数与析构函数的定义。
3. 继承的方式
【实验内容】
1. 习题 4.8 下面是一个含有派生类的程序:
#include <iostream.h>
class A {
// protected:
private:
int m,n;
public:
void set(int a, int b) { m=a; n=b; }
void show() { cout << m << " " << n <<endl; }
};
class B : public A {
int s;
public:
void sets() { s=m*n ; }
void shows() { cout << s << endl; }
};
void main()
{ B obj;
obj.set(2,3);
obj.show();
obj.sets();
obj.shows();
}
在这个程序中,B类的函数sets()能否访问类A中的变量m和n?为什么?如果在类A中把m和n定义为私有成员,上面的程序能否通过编译?为什么?
2. 习题 4.9建立普通的基类buliding,用来存储一座楼房的层数、房间数以及它的总平方米数。建立派生类house,继承building,并存储下面的内容:卧室与浴室外的数量。另外,建立派生类office,继承building,并存储玉器灭火器与电话的数目。
#include <iostream.h>
class building{
protected:
int fr;
int r;
double m;
public:
building(int a, int b, double x)
{ fr=a; r=b; m=x; }
};
class house : public building {
private:
int br;
int dr;
public:
house(int a, int b, double x, int c, int d):building(a,b,x)
{ br=c; dr=d; }
void show()
{ cout << "楼房层数 :"<<fr<<endl;
cout << "房间数 :"<<r<<endl;
cout << "楼房总面积:"<<m<<endl;
cout << "卧室数量 :"<<br<<endl;
cout << "浴室数量 :"<<dr<<endl;
}
};
class offic : public building {
private:
int mr;
int tr;
public:
offic(int a, int b, double x, int c, int d):building(a,b,x)
{ mr=c; tr=d; }
void show()
{ cout << "楼房层数 :"<<fr<<endl;
cout << "房间数 :"<<r<<endl;
cout << "楼房总面积:"<<m<<endl;
cout << "灭火器数量:"<<mr<<endl;
cout << "电话数量 :"<<tr<<endl;
}
};
void main()
{ house ob1(10,200,300000,500,300);
offic ob2(18,560,2500000,1500,600);
cout <<"住宅楼"<<endl;
ob1.show();
cout <<"\n办公大楼"<<endl;
ob2.show();
}
3. 习题 4.10 已知下面的程序框架,按注释中的提示补充细节:
#include <iostream.h>
class planet {
protected:
double distance; //行星到太阳的距离
int revolve; //行星绕太阳转的天数
public:
planet(double d, int r)
{ distance=d; revolve=r; }
};
class earth:public planet{
double circumference; //地球轨道圆周长
public:
earth(double d, int r):planet(d,r)
{ circumference=2*d*3.1416; }
void show()
{ cout <<"地球到太阳的距离:" <<distance<<endl;
cout <<"地球绕太阳转的天数:" <<revolve<<endl;
cout <<"地球轨道圆周长:" <<circumference<<endl;
}
};
void main()
{ earth ob(93000000,365);
ob.show();
}
四. 熟悉C++的多态性
【实验目的】
1. 熟悉函数重。
2. 熟悉运算符重载。
3. 虚函数、纯虚函数和抽象类。
【实验内容】
1. 习题 5.5 针对给出类:three_d类,重载“+”、“++”与“- -”运算符(对于“++”与“- -”只重载前缀方式)。
#include <iostream.h>
class three_d{
int x,y,z;
public:
three_d (int i, int j, int k)
{ x=i; y=j; z=k;}
three_d ( )
{ x=0; y=0; z=0;}
void get(int &i, int &j, int &k)
{ i=x; j=y; k=z; }
friend three_d operator+(three_d a, three_d b)
{ three_d temp;
temp.x=a.x+b.x;
temp.y=a.y+b.y;
temp.z=a.z+b.z;
return temp;
}
three_d operator++()
{ ++x; ++y; ++z; return *this; }
three_d operator--()
{ --x; --y; --z; return *this; }
};
void main()
{ three_d ob1;
three_d ob2(10,20,30);
three_d ob3(100,200,300);
three_d ob4,ob5,ob6;
int ii, jj, kk;
ob1.get (ii,jj,kk);
cout <<"ob1: "<<ii<<','<<jj<<','<<kk<<endl;
ob2.get (ii,jj,kk);
cout <<"ob2: "<<ii<<','<<jj<<','<<kk<<endl;
ob3.get (ii,jj,kk);
cout <<"ob3: "<<ii<<','<<jj<<','<<kk<<endl;
ob4=ob2+ob3;
ob4.get (ii,jj,kk);
cout <<"ob4: "<<ii<<','<<jj<<','<<kk<<endl;
ob5=++ob2;
ob5.get (ii,jj,kk);
cout <<"ob5: "<<ii<<','<<jj<<','<<kk<<endl;
ob6=--ob3;
ob6.get (ii,jj,kk);
cout <<"ob6: "<<ii<<','<<jj<<','<<kk<<endl;
}
2. 习题 5.8利用C++的多态性编写一个简单的学校管理程序,处理的信息包括教师、学生和职员等三类对象。
/*异质链表实现:有三个类 student,teacher,staff,再定义一个
链表类,此类用来存放这几个不同类的对象,并将链表类 list 声明为所有这些类的友元,使它们可以访问它们的私有成员。*/
#include <iostream.h>
#include <string.h>
#include <conio.h>
class person{ //定义一个共同的基类,它具有公共的数据成员
friend class list; //链表类作为本类的友元
protected:
char name[20]; //定义姓名
int age; //定义年龄
char add[40]; //定义地址
char tele[20]; //定义电话号码
static person *ptr; //定义一个指向person类对象的静态指针
person *next; //指向下一个对象的指针
public:
person(char *name,int age,char *add,char *tele);//构造函数
virtual void print(); //说明虚函数
virtual void insert(){};
//定义虚函数并且什么也不做,只定义一个接口
};
class student:public person{ //派生类 student
friend class list; //链表类作为本类的友元
int level; //定义年级
float grade_point_average; //定义平均分
public:
student(char *name,int age,char *add,char *tele, int level,
float grade_point_average); //声明构造函数
void print(); //重新定义print()函数
void insert(); //重新定义insert()函数
};
//
class teacher:public person{ //派生类 teacher
friend class list; //链表类作为本类的友元
float salary; //定义工资
public:
teacher(char *name,int age,char *add,char *tele,float salary);
//声明构造函数
void print(); //重新定义print()函数
void insert(); //重新定义insert()函数
};
//
class staff:public person{ //派生类 staff
friend class list; //链表类作为本类的友元
float hourly_wages; //定义计时工资
public:
staff(char *name,int age,char *add,char *tele,
float hourly_wages); //声明构造函数
void print(); //重新定义print()函数
void insert(); //重新定义insert()函数
};
//
class list{ //定义异质链表类
person *root; //链表头指针
public:
list(){ root=0; } //链表构造函数,初始为 0
void insert_person(person *node); //向链表插入一个对象结点
void remove(char *name); //从链表移去一个对象结点
void print_list(); //输出整个链表
};
//
person::person(char *name,int age,char *add,char *tele)
{ //person 的构造函数
strcpy(person::name,name);
strcpy(person::add,add);
strcpy(person::tele,tele);
person::age=age;
next=0;
}
void person::print()//基类的虚成员函数print()版本,输出基类数据成员
{
cout<<"\nname: "<<name<<"\n";
cout<<"age: "<<age<<"\n";
cout<<"address: "<<add<<"\n";
cout<<"telephone number: "<<tele<<"\n";
}
//
student::student(char *name,int age,char *add,char *tele, int level,
float grade_point_average):person(name,age,add,tele)
{ //student 派生类的构造函数,需缀上基类的构造函数
student::level=level;
student::grade_point_average=grade_point_average;
}
void student::print() //派生类 student的成员函数print()新版本
{
person::print();
cout<<"grade point average: "<<grade_point_average<<"\n";
}
void student::insert()
{ //将 student 类的一个对象赋给 ptr 基类指针,这是允许的
ptr=new student(name,age,add,tele,level,grade_point_average);
}
//
teacher::teacher(char *name,int age,char *add,char *tele, float salary):person(name,age,add,tele)
//teacher 派生类的构造函数,需缀上
//基类的构造函数
{
teacher::salary=salary;
}
void teacher::print() //派生类 teacher的成员函数print()新版本
{
person::print();
cout<<"salary: "<<salary<<"\n";
}
void teacher::insert()
{ //将 teacher类的一个对象赋给 ptr 基类指针,这是允许的
ptr=new teacher(name,age,add,tele,salary);
}
//
staff::staff(char *name,int age,char *add,char *tele, float hourly_wages):person(name,age,add,tele)
//staff 派生类的构造函数,需缀上基类的构造函数
{
staff::hourly_wages=hourly_wages;
}
void staff::print() //派生类 staff的成员函数print()新版本
{
person::print();
cout<<"hourly_wages: "<<hourly_wages<<"\n";
}
void staff::insert()
{ //将 staff类的一个对象赋给 ptr 基类指针,这是允许的
ptr=new staff(name,age,add,tele,hourly_wages);
}
//
void list::insert_person(person *node)
{ //向异质链表插入一个对象,可能是student或teacher或staff
char key[20];
strcpy(key,node->name); //将需插入的对象的姓名赋给key
person *curr_node=root;
person *previous=0; //定义二个查找指针
while(curr_node !=0 && strcmp(curr_node->name,key)<0)
{ //用 curr_node在前,previous在后及 key 循环查找插入位置,由 name
//字符串升序排序,strcmp(串1,串2)<0 表示串1<串2,继续找
previous=curr_node;
curr_node=curr_node->next;
}
node->insert();//由于 insert() 是虚函数,它会根据 node 指针的对象调
//用不同的版本:ptr=new <三个派生类名之一>(数据成员变量表),
//而数据成员变量是由构造函数赋值的,由 new 特性: int *s;
// s=new int(20); 会开辟空间并把值20给空间,因此说:
//调用虚函数将待插入对象赋给 ptr 所指向的单元
node->ptr->next=curr_node;
if(previous==0)
root=node->ptr;
else
previous->next=node->ptr;
//以上三句是把 ptr 指的结点插入链中,这里:node->ptr, previous->ptr,
//curr_node->ptr统统都是一样的,即相当person::ptr 说明ptr 受限的
//对象是stu::ptr或tea::ptr或sta::ptr,因此 node->ptr->next 可写成
// (node->ptr)->next 或 person::ptr->next 但不能把 node->name 写成
// person::name,它只能出现在构造函数中
}
void list::remove(char *name)
{ //从异质链表中移去一对象,此对象用关键字 name 去匹配
person *curr_node=root;
person *previous=0;
while(curr_node !=0 && strcmp(curr_node->name,name)!=0)
{ //用循环寻找此对象
previous=curr_node;
curr_node=curr_node->next;
}
if(curr_node!=0 && previous==0) //若此对象为链表中的头结点
{
root=curr_node->next;
delete curr_node;
}
else if(curr_node!=0 && previous!=0) //此对象为链表中的非头结点
{
previous->next=curr_node->next;
delete curr_node;
}
}
void list::print_list()
{
person *cur=root; //先将头指针赋给 cur
while(cur!=0) //循环输出结点内容并移动指针
{
cur->print(); //根据cur 所指向的对象不同调用不同的 print() 版本
cur=cur->next;
}
}
//
person *person::ptr=0; //为静态变量赋初值
void main()
{
char c;
list people; //定义 list 类的对象 people ,同时调用 list 构造函数,
//也即 root=0;
student stu("刘影liuying",20,"上海shanghai","03578395-456",3,80.0);
//定义 student 类的对象 stu,同时调用 student 构造函数,
//也即先基类 person 构造函数,再自己
teacher tea("李明liming",35,"北京beijing","0105918695-106",560.50);
staff sta("陈林chenling",40,"青岛qingdao","05325895944-335",10);
people.insert_person(&stu);
people.insert_person(&tea);
people.insert_person(&sta);
cout<<"插入结束!\n";
people.print_list();
cout<<"\n以上打印出原链表! 打 c <回车> 继续....";
cin>>c;
people.remove("陈林chenling");
people.remove("李明liming");
people.remove("王迢wangchong");
cout<<"删除结束!\n";
people.print_list();
cout<<"\n以上打印出删除后链表!\n";
}
五. 熟悉C++的模板
【实验目的】
1. 函数模板与模板函数。
2. 类模板和模板类。
【实验内容】
1. 习题 6.3编写一个函数模板,实现两个参量的交换。
# include<iostream.h>
# include<string.h>
template<class AT>
void chang(AT x , AT y )
{ AT z;
z=x; x=y; y=z;
}
/*
void chang(AT *x , AT *y )
{ AT z;
z=*x; *x=*y; *y=z;
}
*/
void main()
{ int i1=10, i2=56;
float f1=12.5, f2=24.5;
double d1=50.344,d2=4656.346;
char c1='k',c2='n';
cout<<"原来 il,i2 是: "<<i1<<','<<i2<<endl;
cout<<"原来 fl,f2 是: "<<f1<<','<<f2<<endl;
cout<<"原来 dl,d2 是: "<<d1<<','<<d2<<endl;
cout<<"原来 cl,c2 是: "<<c1<<','<<c2<<endl;
chang(i1,i2);
chang(f1,f2);
chang(d1,d2);
chang(c1,c2);
/*
chang(&i1,&i2);
chang(&f1,&f2);
chang(&d1,&d2);
chang(&c1,&c2);
*/
cout<<"交换后 il,i2 是: "<<i1<<','<<i2<<endl;
cout<<"交换后 fl,f2 是: "<<f1<<','<<f2<<endl;
cout<<"交换后 dl,d2 是: "<<d1<<','<<d2<<endl;
cout<<"交换后 cl,c2 是: "<<c1<<','<<c2<<endl;
}
2. 习题 6.4建立并演示一个栈类模板。
#include <string>
#include <stdio.h>
#include <conio.h>
//enum bool { false, true };
template<class T>
struct quenode { //定义队列中的各结点类型
T nodedata;
quenode *next;
};
template <class T>
class queue {
protected:
int quesize; //定义队列长度
quenode<T> *head; //定义队列列头
quenode<T> *tail; //定义队列列尾
bool allocateerror;
queue © (queue &q); //队列拷贝函数
public:
queue(); //构造函数
queue(queue &q){ head=NULL;tail=NULL; copy(q);}//建q 队列
~queue() { clearque(); }
bool getallocateerror() { return allocateerror; }
void push(T &); //将结点插入队尾
bool pop(T &); //队头取结点
bool isempty() { return (quesize==0)? true: false;}
//判断队列是否为空
void clearque(); //清空队列
queue &operator= (queue &q) { copy(q); return *this; }
//重载赋值运算符
};
template<class T>
queue<T>::queue() //定义构造函数
{ quesize=0;
allocateerror=false;
head=NULL;
tail=NULL;
}
template<class T>
queue<T> &queue<T>::copy(queue<T> &que)
//将队列que复制给当前队列对象
{ quenode<T> *p, *q, *r;
if (head) clearque();
quesize=que.quesize; //传递队列长度
allocateerror=false;
head=NULL;
tail=NULL;
if (!que.head) return *this; //若队列为空,则返回
head=new quenode<T>; //为队列头结点分配存储空间
if (!head) //若分配失败,则返回
{ allocateerror=true;
return *this;
}
head->nodedata=que.head->nodedata;
//将que队列头结点值赋给当前队列头结点
head->next=NULL;
tail=head; //将队列尾也指向此结点
r=NULL;
p=head; //p 指针也指向此结点
q=que.head->next; //q 指针指向que队列的第二个结点
while (q)
{ r=new quenode<T>; //为结点r 分配存储空间
if(! r )
{ allocateerror=true;
return *this;
}
r->nodedata=q->nodedata;
r->next=NULL;
p->next=r ; //将结点 r 链接到当前队列的链上
tail=r ; //队尾指针指向 r,因为r为最后一个结点
p=p->next; //指针后移
q=q->next;
}
return *this;
}
template<class T>
void queue<T>::push(T &x) //向队尾插入结点
{ quenode<T> *p;
p=new quenode<T>; //为 p 结点分配存储空间
if( !p ) //若分配失败,则返回
{ allocateerror=true;
return;
}
p->nodedata=x;
if (tail) //若队列非空
{ p->next=head; //将 p 结点链接到头指针head前
head =p; //修改队头指针
}
else //若队列为空
{ p->next=NULL;
head=p; //p结点为队列的头,又为尾
tail=p;
}
quesize++; //长度加1
}
template<class T>
bool queue<T>::pop(T &x) //从队头取一结点
{ quenode<T> *p;
if (head) //若队列非空
{ x=head->nodedata; //将队头的数据内容赋给x
p=head;
head=head->next; //修改队头指针
if (head==NULL) //若队列已删空,则tail也改为 NULL
tail=NULL;
delete p; //删除原头结点
quesize--; //长度减1
return true;
}
return false;
}
template<class T>
void queue<T>::clearque() //将队列清空
{ T p;
allocateerror=false;
while (pop(p)) ; //循环提取队列中各结点,实现清除
head=tail=NULL;
};
class staff{ //定义职工类
public:
char name[80];
int age;
float salary;
char sex[8];
void assign(char *name, int age, float salary, char *sex)
{ strcpy(staff::name,name);
staff::age=age;
staff::salary=salary;
strcpy(staff::sex,sex);
}
void print()
{ printf("%10s%6d%10.2f%8s\n",name,age,salary,sex); }
};
void viewque(queue<staff> &que)//显示实例化后的队列que中的各结点
{ int i=1;
staff p;
queue<staff>quecopy(que);
//clrscr();
while(quecopy.pop(p)) //循环提取队列中各结点
{ // gotoxy(1, i+5);
printf("%2d:", i++);
p.print();
}
}
void main()
{ queue<staff> que; //定义队列类对象que
staff p; //定义职工类对象p
p.assign("陈伟林",47,1500,"男"); //给p对象赋值
que.push(p); //将 p 对象压入队列que
p.assign("王凌",34,850.5,"男");
que.push(p);
p.assign("张大霖",27,1200,"男");
que.push(p);
p.assign("方丽碧",51,2000,"女");
que.push(p);
viewque(que); //显示队列中的各结点
}
六. 熟悉C++的I/O流
【实验目的】
1. 用ios 类的成员函数进行格式控制。
使用操作符进行输入输出格式控制。
3. 文件的输入输出
【实验内容】
1. 习题 7.7编写一个程序,打印2~100之间数字的自然对数与以10为底的对数,对表进行格式化,使数字可以在域宽为10的范围内,用小数位数占5位的精度进行右对齐。利用控制输入输出格式的成员函数建立对齐的数字表。
#include<iostream.h>
#include<math.h>
void main()
{ double x;
cout<<" x loge(x) log10(x)\n\n";
cout.precision(6);
cout.setf(ios::right);
for(x=2.0;x<=20.0;x++)
{ cout.width(10);
cout<<x<<' ';
cout.width(10);
cout<<log(x)<<' ';
cout.width(10);
cout<<log10(x)<<'\n';
}
}
/*
#include<iostream.h>
#include<iomanip.h>
#include<math.h>
void main ( )
{ double x;
cout<<setprecision(6);
cout << " x loge(x) log10(x)\n\n";
for (x=1.;x<=20.0;x++)
{
cout <<setw(10)<<x<<' ';
cout <<setw(10)<<log(x)<<' ';
cout <<setw(10)<<log10(x)<<'\n';
}
}
*/
2. 习题 7.8建立类box,保存正方形的尺寸。建立重载输出运算符函数,在屏幕上显示正方形(使用您愿意的方法)。
//下面的程序建立了类box,用来存储正方形的边。
//这个类的重载输出运算符函数在屏幕上显示正方形。
# include<iostream.h>
class box{
int leg;
public:
box (int a) { leg=a; }
friend ostream &operator << (ostream &stream, box ob);
};
ostream &operator<<(ostream &stream, box ob)
{ int i,j;
for(i=1;i<=ob.leg;i++)
stream<<"* ";
stream<<endl;
for(i=2;i<ob.leg;i++)
{ for(j=1;j<=ob.leg;j++)
if((j==1)||(j==ob.leg))
stream<<"* ";
else
stream<<" ";
stream<<endl;
}
for(i=1;i<=ob.leg;i++)
stream<<"* ";
stream<<endl;
return stream;
}
void main ()
{ char a;
box t1(5),t2(10),t3(12);
cout<<t1<<endl;
cin>>a;
cout<<t2<<endl;
cin>>a;
cout<<t3<<endl;
}
3. 习题 7.9编写一个程序,用于拷贝文本文件。让这个程序计算被拷贝的字符的数量,并显示这个结果。
# include <iostream.h>
# include <fstream.h>
int main(int argc, char *argv[ ])
{ char ch;
int sum=0;
if (argc!=3)
{ cout<<"Bad commond !\n"; return 0; }
ifstream inf(argv[1]);
if (! inf)
{ cout<<"Cannot open souce file."; return 1; }
ofstream outf(argv[2]);
if (! outf)
{ cout<<"Cannot open target file."; return 1; }
while (inf)
{ inf.get (ch); sum=sum+1;
outf.put (ch);
}
inf.close ( );
outf.close ( );
cout<<"文件:"<<argv[1]<<" 拷贝到文件:"<<argv[2]<<" 字符数是:"<<sum-1<<endl;
return 0;
}
4. 习题 7.10编写一个程序,倒着显示文本文件。
//使用seekg( )函数,倒着显示文件内容。
# include <iostream.h>
# include <fstream.h>
# include <stdlib.h>
main (int argc, char * argv[ ])
{ char ch;
int k, i=1;
if (argc!=2 )
{ cout<<"Usage: LOCATE<filename><loc>\n"; return 1; }
ifstream inf (argv[1] );
if (! inf)
{ cout<<"Cannot open input file. \n"; return 1; }
cout<<"文件的正向内容 :";
while (!inf.eof())
{ inf.get (ch);
cout<<ch;
}
cout<<endl;
inf.close( );
inf.open (argv[1] );
inf.seekg (-1, ios::end);
k=inf.tellg()+1;
cout<<"文件的反向内容 :";
while (i<=k)
{ inf.get (ch);
cout<<ch;
inf.seekg(-2,ios::cur);
i++;
}
cout<<endl;
inf.close ( );
return 0;
}
5. 习题 7.11有一数组:float xy [ ]={1.2,2.3,8.4,2,6.3333,7.566},要求用cout在屏幕上输出数组各元素的值,输出格式为:
###1.20###2.30###8.40
###2.00###6.33###7.57
#include <iostream.h>
#include <iomanip.h>
class arr{
double a[6];
public:
arr initarr(double ai[6]);
friend ostream &operator<<(ostream &, arr &);
};
arr arr::initarr(double ai[6])
{ int i;
for(i=0;i<6;i++)
this->a[i]=ai[i];
return *this;
}
ostream &operator<<(ostream &output, arr &obj)
{ int i;
// output<<setprecision(5)<<setiosflags(ios::right)<<setw(7)<<setfill('#');
for(i=0;i<6;i++)
{
output<<setprecision(3)<<setiosflags(ios::right)<<setw(6)<<setfill('#')<<obj.a[i];
if(i==2) output<<endl;
}
output<<endl;
return output;
}
void main()
{ double b[6]={1.20,2.30,8.40,2.00,6.3333,7.566};
arr ob;
ob.initarr(b);
cout<<ob;
}
6.习题 7.12把10个浮点数写入一个二进制文件。写一段程序,从键盘接收一个整数,该整数是n,就从该二进制文件中取出第n个浮点数的值,并输出。
//用write( )函数向文件test中写入浮点数。用read( )函数读取指定的数。
# include <iostream.h>
# include <fstream.h>
# include <string.h>
main( )
{ float a[10]; int i,n;
ofstream out("test");
if (! out)
{ cout<<"cannot open output file .\n"; return 1; }
cout<<"输入10个实数:";
for(i=0;i<10;i++)
{ cin>>a[i];
out.write ((char * )&a[i],sizeof (float ));
}
out.close ( );
cout<<'\n'<<"取第几个实数?";
cin>>n;
ifstream in("test");
if (! in) { cout<<"cannot open input file .\n"; return 1; }
float num;
for(i=0;i<10;i++)
{
in.read ((char * )&num, sizeof (float));
if(i==n-1) cout<<num<<" ";
}
cout<<endl;
in.close ( );
return 0;
}
7. 习题 7.13从键盘上输入5个学生的数据(包括学号、姓名、年龄、三门功能的分数),然后求出每个人的平均分数,把学号、姓名和平均分数输出到磁盘文件STUD.REC中,最后从STUD.REC文件中读出这些数据,并在屏幕上显示出来。
# include <fstream.h>
# include <iomanip.h>
int main ()
{ struct student
{ int snum;
char *name;
float score[3];
float avg;
};
struct student s[5];
int i;
ofstream fout("STUD.REC");
if(!fout)
{ cout<<"cannot open output file .\n"; return 1; }
for(i=0;i<5;i++)
{ cout<<"\n输入第"<<i+1<<"号学生的学号:";
cin>>s[i].snum;
cout<<" 输入学生的姓名:";
s[i].name=new char[20];
cin>>s[i].name;
cout<<" 输入三门课的成绩:";
cin>>s[i].score[0]>>s[i].score[1]>>s[i].score[2];
s[i].avg=(s[i].score[0]+s[i].score[1]+s[i].score[2])/3;
fout<<s[i].snum<<' '<<s[i].name<<' '<<s[i].avg<<' ';
}
fout.close ();
ifstream fin("STUD.REC");
if(!fin)
{ cout<<"cannot open input file .\n"; return 1; }
struct student outs;
outs.name=new char[20];
for(i=0;i<5;i++)
{ fin>>outs.snum>>outs.name>>outs.avg;
cout<<"\n第"<<i+1<<"号学生的学号是:";
cout<<outs.snum;
cout<<"\n 姓名是:";
cout<<outs.name;
cout<<"\n 平均分成绩是:";
cout<<outs.avg<<endl;
}
cout<<endl;
fin.close();
return 0;
}