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 &copy (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; }