一、 上机目的和要求
a. 实验四
- 掌握指针的定义和使用。
- 掌握指针在函数参数传递中的作用。
- 掌握引用的作用、定义及使用。
- 掌握结构体类型变量、结构体类型指针及结构体数组的定义和使用。
- 熟悉联合体的概念与应用,了解联合型、枚举型的定义与使用。
- 使用构造类型数据解决实际应用问题,优化程序设计。
b. 实验五
- 掌握类定义的基本格式和类成员的访问控制。
- 掌握对象定义和利用构造函数对对象的数据成员进行初始化的方法。
- 掌握成员函数的定义与使用。
- 理解并掌握静态数据成员和静态成员函数的使用方法。
- 理解友元函数的使用方法。
c. 实验六
- 掌握类的概念、定义格式、类与结构的关系、类的成员属性和类的封装性;
- 掌握类对象的定义;
- 理解类的成员的访问控制的含义,公有、私有和保护成员的区别;
- 掌握构造函数和析构函数的含义与作用、定义方式和实现,能够根据要求正确定义和重载构造函数。能够根据给定的要求定义类并实现类的成员函数;
二、 基本知识和原理
a. 实验四
(一)指针
1. 指针和指针变量
所谓指针就是变量、数组、函数等的存储地址。
指针变量就是用于存放指针的变量。
直接按变量地址存取变量值的方式为直接访问方式。
将变量地址存放在另一个变量(即指针变量)中,再通过指针变量来存取变量值的方式为间接访问方式。
指针变量可以存储各类标识符的地址,包括变量、数组、函数、对象,甚至是指针。
同类型的对象取地址的方式却不尽相同:
(1) 一般变量的地址,用取地址符“&”加变量名;
(2) 数组的地址用数组名表示;
(3) 函数的地址用函数名表示。
2. 指针变量作为函数参数
在C++中,有3种类型的指针可以作为函数的参数,它们是:
(1)一般对象(变量)的指针作为函数的参数;
(2)数组的指针(字符串的指针)作为函数的参数;
(3)函数的指针作为函数的参数。
第一种情况是地址参数。第二种情况是指针作为参数非常多的一种情况,它体现出指针作为参数的优势就是简单、灵活、高效。第三种情况较复杂,主要是为了设计出一个更通用的函数。
(二)结构体、共用体和枚举
1. 构造数据类型的定义
(1) 结构体类型定义的一般形式为:
struct 结构体类型名
{
类型标识符1 成员1;
类型标识符2 成员2;
…
类型标识符n 成员n;
};
每个成员名前的类型标识符可以是已经定义了的任意类型,当然也可以是结构体类型标识符,即结构体成员可以是另一个结构体变量。成员也可以是指向本结构体类型的指针变量。
(2) 联合体和结构体的定义形式相似,使用了union关键字。
在作用域范围内,结构体对象的每个数据成员都有固定的存储位置,都可以随时被访问,而联合体对象的每个成员都从同一个位置(即对象的首地址)开始存储,在任一时刻只能保存一个数据成员,因而也只有该成员能够被访问,当然在不同的时刻可以用联合体对象存储不同的成员并进行相应的访问。
(3) 枚举类型和枚举变量的定义形式为:
enum<枚举类型名>{<枚举表>};
或
enum{<枚举表>}<变量名表>;
枚举类型定义中每个枚举元素代表一个整数值。如果定义时枚举元素未指定值,编译系统按定义顺序取默认值一次为0,1,2,3,……。也可以给枚举值指定对应值。
2. 构造类型变量的定义及初始化
(1) 结构体变量的定义及初始化有3种形式:
① 结构体类型名定义结构体变量
[struct] <结构体类型名> <结构体变量名>[={<初始化数据>}|<结构变量名>],…;
② 定义结构体类型的同时定义结构体变量,
struct 结构体类型标识符
{
类型标识符1 成员名1;
类型标识符2 成员名2;
…
类型标识符n 成员名n;
} <结构体变量名>[={<初始化数据>}|<结构变量名>],…;
③ 定义无名结构体类型的同时定义结构体变量
struct
{
类型标识符1 成员名1;
类型标识符2 成员名2;
…
类型标识符n 成员名n;
} <结构体变量名>[={<初始化数据>}|<结构变量名>],…;
结构体类型变量的各个元素一次占用一块连续的内存空间,结构体变量所占的内存长度等于每个成员长度之和,结构体变量占用的内存大小可以用sizeof计算。
(2) 联合体变量的定义同结构体变量一样,也有3种形式,但是使用的关键字为union。
联合体和结构体的定义形式相似,但含义不同,结构体变量所占的内存长度等于个成员所占内存长度之和(即每个成员分别占有自己的内存)。联合体变量所占的内存长度等于最长的成员的长度(即每个成员共享同一段内存)。
不能直接引用联合体变量,也不能在自定义联合体变量的同时进行初始化,而只能引用联合体变量的成员。
(3) 枚举类型的变量定义可以有以下形式。
① 定义类型时定义,例如:
enum day{Sun,Mon,Tue,Wed,Thu,Fri,Sat} d1,d2,d3;
② 直接定义枚举变量,例如:
enum {Sun,Mon,Tue,Wed,Thu,Fri,Sat} d1,d2,d3;
③ 使用枚举类型定义枚举变量,例如:
enum day d1,d2,d3;
或
day d1,d2,d3;
枚举变量只能取花括号中所列出的标识符,而取其他值都是非法的。
3. 构造类型成员的访问
(1) 结构体成员的访问
直接访问结构体的成员:结构体变量名.成员名。
间接访问结构体的成员:结构体指针->成员名。
也可以使用以下形式访问结构体变量:
(*p).num、(*p).name、(*p).score
当用“.”和“->”运算符访问嵌套结构体的成员时,先访问外层后访问内层,逐层处理,例如:
Student1 LiYing,*p=&LiYing;
LiYing.birthday.year=2003;
p->birthday.day=26;
同类型的结构变量可以相互赋值,其运算功能是把右边的变量值复制到左边的变量中,即复制到左边变量对应的存储空间中,运算结果为左边的变量。
(2) 联合体成员的访问
直接访问联合体的成员:联合体变量.成员名。
间接访问联合体的成员:联合体指针->成员名。
注意:不能直接引用联合体变量,也不能在定义联合体变量时进行初始化,而只能引用联合体变量的成员。
(3) 枚举元素的引用
枚举元素的引用不同于结构体成员和联合体成员。它不需要使用“.”和“->”运算符,而是在枚举类型定义之外,可以直接引用。例如:
enum color {red,yellow,green=3,blue} c1;
c1=blue; //枚举元素作为整型变量向枚举变量赋值,可以直接引用
对枚举元素按常量处理,但不能对它们赋值,例如:
red=0; //错误,不能给枚举常量赋值
4. 构造体类型变量作为函数参数
(1) 结构体类型作为函数参数
结构体是一种类型,它能够使用在允许简单类型使用的所有地方,也允许作为函数参数类型和返回值类型。将一个结构体变量的值传递给另一个函数,有3种办法:
① 用结构体变量的成员作实参:将实参值传给形参,属于“值传递”方式,要求实参与形参的类型保持一致。
如,有函数定义
void fun(int num);
则有调用形式
fun(stu.num);
其中,stu.num是结构体变量stu的一个成员,其类型为int。
② 用结构体变量作实参:采用的是“值传递”方式,将结构体变量所占的内存单元的内容全部顺序传给形参。要求形参也必须是同类型的结构体的变量。
如,有函数定义
void fun(student a);
则有调用形式
fun(x);
其中,x是结构体变量,其类型为student。
③ 用指向结构体变量(或数组)的指针作实参:将结构体变量(或数组)的地址传给形参,属于传址调用,在函数中对形参的操作实际上是对实参的操作。
如,有函数定义
void fun(student *P,int n);
void fun1(student *S,int n);
则有调用形式
fun(&x,n);
fun1(stu,n);
其中,x是结构体类型student的结构体变量,stu是结构体类型student的结构体数组名。fun函数调用将结构体变量x的地址传递给形参指针p,然后执行fun的函数体。fun1函数调用将结构体数组stu[]的首地址传递给形参指针s,然后执行fun1的函数体。
注意:函数也可以返回结构体变量、结构体变量的成员或结构体变量的指针。由于用结构体变量作为函数参数和函数返回结构体变量在时间和空间上的开销都比较大,因此使用较少,多用于指向结构体变量的指针来替代。
(2) 联合体作为函数的参数
可以使用联合体变量的成员和指向联合体变量的指针作为函数参数。当使用指向联合体变量的指针时,指针访问的内容视指针指向的成员的类型决定。
不能把联合体变量作为函数参数,也不能让函数带回联合体变量。
(3) 枚举作为函数的参数
枚举元素和枚举变量可以作为函数参数,函数的返回值也可以是枚举类型。枚举类型的作用域与一般变量相同。
b. 实验五
1. 类的定义
在面向对象程序设计中,类(class)就是对现实世界中抽象出的“类”的模拟描述,是用户自己定义的数据类型,它的一般定义格式如下:
class <类名>
{
private:
<私有数据成员和成员函数>;
protected:
<保护数据成员和成员函数>;
public:
<公有数据成员和成员函数>;
};
<类中各个成员函数的实现>
2.类成员的访问控制
类的成员访问权限总体上可分为:公有的(public)、私有的(private)和保护的(protected)三类。若某个成员未作任何访问权限声明,则默认为私有的。
公有的成员用public来说明,这部分通常是一些操作(即成员函数),作为类与外界的接口,所有公有的成员可以在程序中的任何位置被访问。
私有的成员用private来说明,这部分通常是一些数据成员,这些成员用来描叙该类中对象的属性的,只有成员函数或经过特殊说明的函数(如友元函数)才可以引用它们,它们是特意被用户隐藏起来的部分,用户在类外其他地方是无法访问它们的。
保护的成员用protected来说明,它的限定能力介于私有和公有之间,除了类本身的成员函数、友元函数可以访问成员外,只有该类的派生类(子类)可以访问。
关键字public、private和protected被统称为访问权限修饰符或访问控制修饰符。它们在类体(即一对花括号内)出现的先后顺序没有要求,并且允许多次出现。
3. 对象的定义
C++规定:必须先定义类,然后定义对象,用类来定义对象在格式上与普通类型定义变量是完全相同的.
定义对象的一般形式:
<类名> <对象名表>;
4.对象成员的访问方式
对象成员访问的一般形式是:
<对象名>.<数据成员名>
或者
<对象名>.<成员函数名>([<实参表>])
5. 构造函数的定义和分类
构造函数是类的一种特殊的成员函数,它的主要作用于是为对象分配空间和进行初始化工作。除了具有一般成员函数的特征外,还具有以下一些特殊的性质:
(1) 构造函数的名字必须与类名字相同;
(2) 构造函数可以有任意类型和任意个数的参数,所以构造函数可以重载,但不能指定返回值类型;
(3) 构造函数的函数体可以写在类体内,也可以写在类体外;
(4) 构造函数被声明为公有函数,但它不能像其他成员函数那样被显示的调用,而是在用类声明对象的同时被系统自动调用。
调用构造函数的一般形式是:
类名 对象名(参数表);
构造函数分为4类,分别是普通构造函数、默认构造函数、有缺省参数的构造函数和复制(拷贝)构造函数。
6. 默认构造函数
默认构造函数是指没有任何参数的构造函数。如果在设计类时没有定义构造函数,C++编译程序会自动为该类建立一个默认的构造函数。这个默认构造函数没有任何形式参数,并且函数体为空。其格式如下:
<类名>::<默认构造函数名>() { }
按构造函数规定,默认构造函数名与类名相同。默认构造函数也可以由程序员直接定义在类体中。另外,如果构造函数的参数具有默认值时,这样的构造函数被称为有缺省参数的构造函数。
7.拷贝构造函数
拷贝构造函数是一种特殊的构造函数,用于依据已存在的对象建立一个新对象。其一般形式为:
class T
{
public:
T(const T & 对象名);
……
}
T::T(const T &对象名 )
{ 函数体 }
其中,T代表任何一个类的名字。const是一个类型修饰符,被它修饰的对象是不能被改变的常量.
拷贝构造函数的拷贝分为浅拷贝和深拷贝。一般来说,只需浅拷贝时最好利用系统自动生成的拷贝函数,这样效率高。若需要在构造函数中开辟新的内存空间,则需要我们自己编写这样的构造函数以完成深拷贝。
拷贝构造函数主要在下面3种情况中起到初始化作用。
(1) 声明语句中用一个对象初始化另外一个对象,例如:
Person student2 (student1);
(2) 函数的参数是值参数时,若用对象作为函数实参传递给函数形参,这时需要调用拷贝构造函数。
(3) 当对象作为函数返回值时,如执行return R 时,系统将用对象R来初始化一个匿名对象,这时需要调用拷贝构造函数。
8.析构函数
析构函数是类的一种特殊成员函数,其功能是用来释放一个对象的内存空间。它的功能与构造函数正好相反.
析构函数的特点如下:
(1)析构函数是成员函数,函数体可以写在类体内,也可以写在类体外;
(2)析构函数是一个特殊的函数,它的名字是在类名的前面加“~”字符;
(3)析构函数没有参数,没有返回值,所以不能重载。
在下面两种情况下,析构函数会被自动调用:
A.当一个对象的作用域结束时,该对象的析构函数被自动调用。
B.当一个对象是使用new运算符被动态创建的,那么在使用delete运算符释放它时,delete将会自动调用析构函数。
9.重载成员函数
构造函数可以重载,而析构函数不能重载,原因是构造函数可以有参数,析构函数不带任何参数,所以无法重载。可以说,带有不同类型和数量参数的成员函数都可以进行重载。
10.子对象
将一个类的对象作为另一个类的成员,该对象就被称为子对象。
在一个类中出现了子对象时,该类的构造函数就要考虑子对象的初始化问题。在C++中,通常采用初始化列表的方法来初始化子对象。在初始化列表中可以包括对子对象的初始化及对类中其他成员的初始化。
11.指向对象的指针
每个对象在声明后都会在内存中占有一定的空间,就会有地址。因此,可以用指针变量来保存这个地址。指向对象的指针(变量)就是用于存放对象地址的变量。声明对象指针的一般形式为:
<类名>*<对象指针名>;
声明对象指针的语法和声明其他数据类型指针的语法相同。使用对象指针时,首先要把它指向一个已创建的对象,然后才能以“间接方式”访问该对象。
12.this指针
this指针是一个隐含于每个类的成员函数中的特殊指针。该指针是一个指向正在操作的成员函数的对象。当一个对象调用成员函数时,编译程序先将对象的地址赋给this指针,然后调用成员函数。
13.对象数组
对象数组是指将对象作为数组元素的数组。该数组中若干个元素必须是同一个类的若干个对象。对象数组的定义、赋值和引用与普通数组一样,只是数组的元素与普通数组不同,它是具有相同类的若干个对象。
定义一个对象数组的一般形式是:
<类名><数组名>[<数组长度>];
定义二维对象数组的一般形式是:
<类名><数组名>[<第一维数组长度>][<第二维数组长度>];
14.常对象
如果在说明对象时用const修饰,则被说明的对象为常对象。常对象的数据成员值在对象的整个生命期内不能改变,常对象的声明形式如下:
<类名>const<对象名>[(初值)]; 或者
const<类名><对象名>[(初值)];
在定义常对象时必须进行初始化,而且常对象不能被更新。
15.常数据成员
在类中使用关键字const来说明某个数据成员为常数据成员。如果在一个类中说明了常数据成员,那么构造函数就只能通过初始化列表对该数据成员进行初始化,而任何其他函数都不能对该数据成员赋值。
类的常数据成员即可以是常量也可以是常引用,由于必须初始化,因此,类中这些常数据成员必须也只能通过构造函数的成员初始化列表来实现初始化工作。
16.常成员函数
在类中使用关键字const说明的函数称为常成员函数,它的一般说明形式是:
<类型><成员函数名><([参数表])>const;
const是函数类型的一个组成部分,因此在函数的实现部分也要带关键字const。
关于常成员函数的说明:
(1)常成员函数不能调用该类中的普通成员函数,因而也不能更新对象的数据成员;
(2)如果将一个对象设为常对象,则该对象只能调用它的常成员函数,而不能调用普通的成员函数,这是C++在安全机制上的考虑。
17.常类型的函数参数传递
将形参设置为const引用形参或const地址(指针)形参,这样就可以保障安全快捷的传递对象了。将函数形参设为const型引用和指针的一般形式是:
const<类型说明符>&<引用名>
const<类型说明符>*<指针变量名>
18.静态数据成员
C++中同一个类定义多个对象时,每个对象都拥有各自的成员,而静态数据成员是类的所有对象中共享的成员,它不因对象的建立而产生,也不因对象的消失而删除,它是类定义的一部分,属于整个类,即属于所有对象。
由于静态数据成员不专属于任何一个具体对象,但任何一个对象在声明前都需要它提前拥有一个值,因此C++规定:必须对类的静态数据成员初始化,并且它的初始化不能在构造函数中进行。
静态数据成员初始化的方法一般采用如下形式:
<类型><类名>::<静态数据成员>=<值>;
关于静态数据成员初始化的进一步说明:
(1) 初始化在类体外进行,其前面不加static;
(2) 初始化时不加该成员的访问权限控制符private、public或protected;
(3) 即使静态数据成员是私有的,也可以在类外有文件作用域的地方直接初始化,一般在类的定义之后马上初始化。
在引用公有的静态数据成员时采用下面的形式:<类名>::<静态数据成员>
19.静态成员函数
静态成员函数的定义和其他成员函数相同。但它的特点与静态数据成员类似,不专属于任何一个对象,为整个类所共享。静态成员函数的定义方法是在一般成员函数的定义前面加上static关键字。
关于静态成员函数的说明如下:
(1) 调用静态成员函数的格式一般采用如下形式:
<类名>::<静态成员函数名>(<参数表>);
(2) 静态成员函数只能访问静态数据成员、其他静态成员函数和类以外的函数与数据,不能访问类中的非静态数据成员,因为非静态数据成员只有对象存在时才有意义。
(3) 静态成员函数不能声明为虚函数。
20.友元
友元是一种定义在类外部的普通函数或类,但它需要在类体内声明为“朋友”。友元的作用在于提高程序的运行效率,但是从某种程度上讲,它破坏了类的封装性和隐藏性,使得非成员函数可以访问类的私有成员,因此在程序设计时应该严格限制使用。
友元可以是一个函数,称之为友元函数,它也可以是一个类,该类被称为友元类。下面分别介绍这两种友元。
(1) 友元函数
为了与该类的成员函数加以区别,在类内说明友元函数时需要在前面加上关键字friend。需要注意的是友元函数不是成员函数,但是它可以访问类中的私有成员。
定义友元函数的方式是在类定义中用关键字friend说明该函数,其格式如下:
friend<类型><友元函数名>([<参数表>]);
说明:友元函数说明的位置可以在类中的任何位置,意义完全一样。友元函数定义则必须在类的外部,一般与类的成员函数定义在一起。声明类的友元函数的目的就是为普通函数提供直接方便的访问该类的所有成员的权限。
(2) 友元类
友元也可以是一个类,即将一个类作为另一个类的友元。当一个类作为另一个类的友元时,意味着这个类的所有成员函数都是另一个类的友元函数。
c. 实验六
通过建立类及对象,用类的成员函数和对象访问类的成员;
利用建立类的构造函数,完成类的成员的初始化工作;
三、 程序算法分析及实现(代码)(或问题解答)
a. 实验四
【实例1】 指针变量的定义和使用。
#include <iostream>
using namespace std;
int main()
{
int i=10;
int *ip;
ip=&i;
cout<<"the address of i is"<<ip<<endl;
cout<<"the value of i is" <<*ip<<endl;
return 0;
}
【实例2】 指针的运算。
#include <iostream>
using namespace std;
int main()
{
int a=10,b=20;
int *pa,*pb;
pa=&a;
pb=&b;
cout<<"the value in the addres pa"<<*pa<<endl;
cout<<"the value in the addres pa+1"<<*(pa+1)<<endl;
if(pa!=pb)
cout<<"addres pa != addres pb"<<endl;
return 0;
}
【实例3】 指向常量的指针变量的使用。
#include <iostream>
using namespace std;
int main()
{
const double *ip;
double i1=123.456;
double i2=234.567;
ip=&i1;
//*ip=1.25; //错误,该地址的值不能改变
ip=&i2; //正确,对指针重新赋值
cout<<*ip<<endl;
return 0;
}
【实例4】 用指针法访问一维数组和二维数组。
#include <iostream>
using namespace std;
int main()
{ int i,j,*p,a[5];
int (*q)[3],b[3][3]={{1,2,3},{4,5,6},{7,8,9}};
char str[]="I love China!";
char *r=str;
p=a;
q=b;
cout<<"input five numbers";
for(i=0;i<5;i++)
cin>>*(p+i);
cout<<"One-dimensional array: ";
for(i=0;i<5;i++)
cout<<*(p+i)<<" ";
cout<<endl;
cout<<endl;
cout<<"Two-dimensional array: "<<endl;
for(i=0;i<3;i++)
{ for(j=0;j<3;j++)
cout<<*(*(q+i)+j)<<" ";
cout<<endl;
}
cout<<endl;
cout<<"strings: ";
while(*r!='\0')
{ cout<<*r;
r=r+1;
}
cout<<endl;
return 0;
}
【实例5】 用指针作函数的参数,完成三个整数值的循环交换。
#include <iostream>
using namespace std;
void circlechange(int *x,int *y,int *z);
int main()
{ int a,b,c;
cout<<"Please input three integer numbers:"<<endl;
cin>>a>>b>>c;
cout<<"before calling:"<<"a="<<a<<",b="<<b<<",c="<<c<<endl;
circlechange(&a,&b,&c);
cout<<"after calling:"<<"a="<<a<<",b="<<b<<",c="<<c<<endl;
return 0;
}
void circlechange(int *x,int *y,int *z)
{ int temp;
temp=*x;
*x=*y;
*y=*z;
*z=temp;
}
【实例6】 引用的使用。
#include <iostream>
using namespace std;
int main()
{ int a=20,b=40;
int &ra=a; //定义引用ra为a的别名
int *p=&b; //定义指针p指向b
int *&rp=p; //定义引用rp为p的别名
cout<<"a="<<ra<<endl; //ra与a等价
cout<<"b="<<*rp<<endl; //rp与p等价
return 0;
}
【实例7】 引用与指针的比较。
//用引用作形参
#include <iostream>
using namespace std;
void swap(int &p1, int &p2) //引用作形参
{ int p;
p=p1;
p1=p2;
p2=p;
}
int main()
{ int a,b;
cout<<"Input two integers:"<<endl;
cin>>a>>b;
swap(a,b); //直接以变量作实参
cout<<a<<' '<<b<<endl;
return 0;
}
//用指针作形参
#include <iostream>
using namespace std;
void swap(int *p1, int *p2) //指针作形参
{ int p;
p=*p1; //必须用“*指针变量名”的形式操作目标数据
*p1=*p2;
*p2=p;
}
int main()
{ int a,b;
cout<<"Input two integers:"<<endl;
cin>>a>>b;
swap(&a,&b); //必须以变量的地址作实参
cout<<a<<' '<<b<<endl;
return 0;
}
【实例8】 调试下列程序,找出其中的错误,记录并分析出错信息,然后改正并记录运行结果。
#include <iostream>
using namespace std;
int main()
{
struct student
{
int num;
char name[20];
int age;
}student1;
student1={1001,"Li Nan",15};
cout<<student1.num<<endl;
return 0;
}
#include <iostream>
using namespace std;
int main()
{
union Data
{ int i;
char ch;
float f;
};
Data a={.i=1002};
a={.ch='A'};
a={.f=2.0};
cout<<a.f<<endl;
return 0;
}
【实例9】调试并运行以下程序,领会程序设计的基本思路和关键知识点。
程序实现的基本功能如下:
(1)定义结构体类型Student,记录每个学生的数据,包括学号、姓名、3门课的成绩、平均成绩和总成绩。
(2)在主函数中定义结构体数组stu[M],M定义为常量。使用菜单的形式调用函数实现对学生成绩的管理。
(3)调用函数实现从键盘输入n个学生的信息,包括学号、姓名、3门课的成绩。函数原型为:
void input(Student a[],int n);
(4)调用函数实现学生平均成绩和总成绩的运算,函数原型为:
void count(Student a[],int n);
(5) 调用函数实现学生成绩信息的输出,函数原型为:
void output(const Student a[],int n);
#include <iostream>
using namespace std;
const int M = 100;
struct Student
{
int num;
char name[20];
int first_goal;
int second_goal;
int third_goal;
float average_goal;
int count_goal;
}stu[M];
void input(Student a[],int n);
void count(Student a[],int n);
void output(const Student a[],int n);
int main()
{
int n;
cout << "input the number " << endl;
cin >> n;
if(n>0)
{
for (int i = 0; i < n;i++)
input(stu,i);
count(stu, n);
for (int i = 0; i < n;i++)
output(stu,i);
}
return 0;
}
void input(Student a[],int n)
{
cout << "input the number " << endl;
cin >> a[n].num;
cout << "input the name " << endl;
cin >> a[n].name;
cout << "input the first goal " << endl;
cin >> a[n].first_goal;
cout << "input the second goal " << endl;
cin >> a[n].second_goal;
cout << "input the third goal " << endl;
cin >> a[n].third_goal;
a[n].count_goal = a[n].first_goal + a[n].second_goal + a[n].third_goal;
a[n].average_goal = (a[n].count_goal)*1.0 / 3;
}
void count(Student a[],int n)
{
float all_average_goal;
int all_count_goal=0;
for (int i = 0; i < n;i++)
all_count_goal += a[i].count_goal;
all_average_goal = 1.0 * (all_count_goal) / n;
cout << "All students score in total is " << all_count_goal << endl;
cout << "All students average score in total is " << all_average_goal << endl;
}
void output(const Student a[],int n)
{
cout << "the" << n << "student\n" << endl;
cout <<"the number:"<< a[n].num << endl;
cout <<"name: "<< a[n].name <<endl;
cout <<"first goal: "<< a[n].first_goal << endl;
cout <<"second goal: "<< a[n].second_goal <<endl;
cout <<"third goal: "<< a[n].third_goal <<endl;
cout <<"average goal: "<< a[n].average_goal << endl;
cout <<"count goal: "<< a[n].count_goal << endl;
}
【实例10】 完善上述程序,写出实现以下功能的代码并上机调试。
(1)采用冒泡法排序,将学生总成绩按照从大到小的顺序排列。
编程提示:函数的原型为void sort(Student a[],int n),在主函数中将结构体数组及学生人数传递给函数形参s和n,在函数中使用冒泡法排序对总成绩进行排序,注意比较时使用总成绩s[j].sum,交换时使用结构体变量对记录进行整体操作,体会与实现数组排序的区别。函数sort的参考代码为:
void sort(Student s[],int n)
{
int i,j;
Student temp;
for(i=1;i<=n-1;i++)
for(j=0;j<=n-i-1;j++)
if(s[j].sum<s[j+1].sum)
{ temp=s[j];
s[j]=s[j+1];
s[j+1]=temp;
}
cout<<“按总成绩排序完毕!”<<endl;
}
代码实现:
将函数声明void sort(Student a[],int n);加入到Lab4.h文件中;
将函数的定义加入到Lab4.cpp文件中;
在主程序main.cpp中加入调用该函数的代码。
(2)对排序后的数组采用折半查找法,按学号查找到对应的记录,找到后返回找到记录的指针,否则返回0。
编程提示:
用函数来实现,假定函数名为findNum,返回值类型为结构体指针类型(Student *),即被查找的元素的地址。函数参数有3个,分别为结构体指针、数组长度和要查找的学号,函数原型定义为:
Student * findNum(Student s[],int n,int x);
其中,参数x为要查找的学号。此题的查找过程是从已排序的数组s中,采用折半查找,若找到s[i].num的值与x的值相等,就返回当前记录的地址,否则返回NULL。函数findNum的参考代码为:
Student *findNum(Student s[],int n,int x)
{
//按照学号进行由小到大的排序
int low=0,high=n-1;
int mid;
while(low<=high)
{
mid=(low+high)/2;
if(s[mid].numx)
return &s[mid];
else if(s[mid].num<x)
low=mid+1;
else
high=mid-1;
}
return NULL;
}
在主程序中,实现对函数的调用,调用时完成如下功能:输入待查学号,调用函数实现查找,查找结束后,提示是否查找其他学号,回答“Y”,继续输入学号进行查找,回答“N”,返回到主菜单。
根据返回值判断,若为NULL则输出没找到,若找到则输出找到的记录。参考代码如下:
long x;
Student *p;
char ch=‘Y’;
//注意:以上三条语句不可以添加在case 5:中,要添在main函数的开头部分
while(ch’Y’||ch==‘y’)
{
cout<<“请输入一个待查学生的学号:”;
cin>>x;
p=findNum(stu,n,x);
if(p!=NULL)
{
cout<num<<’,’<name<<’,’;
for(int j=0;j<3;j++)
cout<score[j]<<’,’;
cout<sum<<","<ave<<endl;
}
else
cout<<“没有找到学号为”<<x<<“的记录”<<endl;
cout<<“是否继续查找(Y/N)?”;
cin>>ch;
}
代码实现:
将函数声明Student *findNum(Student s[],int n,int x);加入到Lab4.h文件中;
将函数的定义加入到Lab4.cpp文件中;
在主程序main.cpp中加入调用该函数的代码。
Work.cpp:
#include <iostream>
#include "lab4.h"
using namespace std;
void input(Student a[],int n);
void count(Student a[],int n);
void output(const Student a[],int n);
int main()
{
int n;
cout << "input the number " << endl;
cin >> n;
if(n>0)
{
for (int i = 0; i < n;i++)
input(stu,i);
cout << "start sort in goal" << endl;
count(stu, n);
sort(stu, n);
for (int i = 0; i < n;i++)
output(stu,i);
}
return 0;
}
void input(Student a[],int n)
{
cout << "input the number " << endl;
cin >> a[n].num;
cout << "input the name " << endl;
cin >> a[n].name;
cout << "input the first goal " << endl;
cin >> a[n].first_goal;
cout << "input the second goal " << endl;
cin >> a[n].second_goal;
cout << "input the third goal " << endl;
cin >> a[n].third_goal;
a[n].count_goal = a[n].first_goal + a[n].second_goal + a[n].third_goal;
a[n].average_goal = (a[n].count_goal)*1.0 / 3;
}
void count(Student a[],int n)
{
float all_average_goal;
int all_count_goal=0;
for (int i = 0; i < n;i++)
all_count_goal += a[i].count_goal;
all_average_goal = 1.0 * (all_count_goal) / n;
cout << "All students score in total is " << all_count_goal << endl;
cout << "All students average score in total is " << all_average_goal << endl;
}
void output(const Student a[],int n)
{
cout << "the" << n+1 << "student\n" << endl;
cout <<"the number:"<< a[n].num << endl;
cout <<"name: "<< a[n].name <<endl;
cout <<"first goal: "<< a[n].first_goal << endl;
cout <<"second goal: "<< a[n].second_goal <<endl;
cout <<"third goal: "<< a[n].third_goal <<endl;
cout <<"average goal: "<< a[n].average_goal << endl;
cout <<"count goal: "<< a[n].count_goal << endl;
}
Lab4.h
#ifndef _LAB4_H
#define _LAB4_H
const int M = 100;
struct Student
{
int num;
char name[20];
int first_goal;
int second_goal;
int third_goal;
float average_goal;
int count_goal;
}stu[M];
void sort(Student s[], int n);
#include "lab4.cpp"
#endif
Lab4.cpp
#include<iostream>
#include "lab4.h"
using namespace std;
void sort(Student s[],int n)
{
int i,j;
Student temp;
for(i=1;i<=n-1;i++)
for(j=0;j<=n-i-1;j++)
if(s[j].count_goal<s[j+1].count_goal)
{ temp=s[j];
s[j]=s[j+1];
s[j+1]=temp;
}
cout<<"over! "<<endl;
}
2
Work.cpp
#include <iostream>
using namespace std;
#include "lab4.h"
void input(Student a[],int n);
void count(Student a[],int n);
void output(const Student a[],int n);
int main()
{
int n;
cout << "input the number " << endl;
cin >> n;
if(n>0)
{
for (int i = 0; i < n;i++)
input(stu,i);
cout << "start sort in num" << endl;
count(stu, n);
sort(stu, n);
for (int i = 0; i < n;i++)
output(stu,i);
char Condition;
do{
cout << "Do you want to find stduent by use number?(Y/N)" << endl;
cin >> Condition;
if(Condition=='Y' || Condition == 'y')
{
int x;
Student *found_x;
cin >> x;
found_x = findNum(stu, n, x);
if(found_x!=NULL)
{
cout << "the " << x << " student\n" << endl;
cout << "the number:" << found_x->num << endl;
cout << "name: " << found_x->name << endl;
cout << "first goal: " << found_x->first_goal << endl;
cout << "second goal: " << found_x->second_goal << endl;
cout << "third goal: " << found_x->third_goal << endl;
cout << "average goal: " << found_x->average_goal << endl;
cout << "count goal: " << found_x->count_goal << "\n" << endl;
}
else
cout << "404 not found" << endl;
}
else
break;
} while (Condition == 'Y' || Condition == 'y');
}
return 0;
}
void input(Student a[],int n)
{
cout << "input the number " << endl;
cin >> a[n].num;
cout << "input the name " << endl;
cin >> a[n].name;
cout << "input the first goal " << endl;
cin >> a[n].first_goal;
cout << "input the second goal " << endl;
cin >> a[n].second_goal;
cout << "input the third goal " << endl;
cin >> a[n].third_goal;
a[n].count_goal = a[n].first_goal + a[n].second_goal + a[n].third_goal;
a[n].average_goal = (a[n].count_goal)*1.0 / 3;
}
void count(Student a[],int n)
{
float all_average_goal;
int all_count_goal=0;
for (int i = 0; i < n;i++)
all_count_goal += a[i].count_goal;
all_average_goal = 1.0 * (all_count_goal) / n;
cout << "All students score in total is " << all_count_goal << endl;
cout << "All students average score in total is " << all_average_goal << endl;
}
void output(const Student a[],int n)
{
cout << "the " << n+1 << " student\n" << endl;
cout <<"the number:"<< a[n].num << endl;
cout <<"name: "<< a[n].name <<endl;
cout <<"first goal: "<< a[n].first_goal << endl;
cout <<"second goal: "<< a[n].second_goal <<endl;
cout <<"third goal: "<< a[n].third_goal <<endl;
cout <<"average goal: "<< a[n].average_goal << endl;
cout <<"count goal: "<< a[n].count_goal << "\n" << endl;
}
b. 实验五
【实例1】定义一个关于日期的类,然后声明对象,判断该日期是否为闰年并输出。
#include <iostream>
using namespace std;
class TDate
{
public :
void SetDate(int y,int m,int d);
int isLeapYear();
void Print();
private:
int year,month,day;
};
void TDate::SetDate(int y,int m,int d)
{
year = y;
month = m;
day = d;
}
int TDate::isLeapYear()
{
return (year%4==0&&year%100!=0) || (year%400==0);
}
void TDate::Print()
{
cout<<year<<"."<<month<<"."<<day<<endl;
}
int main()
{
TDate date1,date2;
date1.SetDate(2004,5,4);
date2.SetDate(2005,4,9);
int leap = date1.isLeapYear();
date1.Print();
if (leap == 1)
cout<<"Yes!"<<endl;
else
cout<<"No!"<<endl;
date2.Print();
return 0;
}
【实例2】编写一个程序,定义一个日期时间类,并在类中定义构造函数和析构函数。
#include <iostream>
using namespace std;
class TDateTime
{
public:
TDateTime(int y,int m,int d,int h,int mi,int s);
~TDateTime();
void Print();
private:
int year,month,day,hour,minite,second;
};
TDateTime::TDateTime(int y,int m,int d,int h,int mi,int s)
{
year = y;
month = m;
day = d;
hour = h;
minite = mi;
second = s;
cout<<"Constructor called."<<endl;
}
TDateTime::~TDateTime()
{
cout<<"Destructor called."<<endl;
}
void TDateTime::Print()
{
cout<<year<<"/"<<month<<"/"<<day<<"/"<<hour<<":"<<minite<<":"<<second<<
endl;
}
int main()
{
TDateTime Now(2007,10,31,9,30,35);
cout<<"Now is ";
Now.Print();
return 0;
}
【实例3】拷贝构造函数的使用
#include <iostream>
#include <string>
#include <cstring>
using namespace std;
class Person
{
public:
Person(char* pN);
Person(Person &p);
~Person();
private:
char * pName;
};
Person::Person(char *pN)
{
cout<<"Constructor: "<<pN<<endl;
pName = new char[strlen(pN)+1];
strcpy(pName,pN);
}
Person::Person(Person &p)
{
cout<<"copy "<<p.pName<<"to new stack space\n";
pName = new char[strlen(p.pName)+1+8]; //8是字符串"Copy of "的长度
strcpy(pName,"Copy of ");
strcat(pName,p.pName);
}
Person::~Person()
{
cout<<"Destructor: "<<pName<<endl;
delete pName;
}
int main()
{
Person p1("Jack");
Person p2(p1); //或者Person p2=p1;
return 0;
}
【实例4】使用静态数据成员。
#include <iostream>
using namespace std;
class Static_Myclass
{
public :
static int a; //静态数据成员a
};
int Static_Myclass::a = 5; //初始化静态数据成员a
int main()
{
Static_Myclass obj1,obj2;
obj1.a = 10; //静态数据成员的引用方法一
Static_Myclass::a = 20; //静态数据成员的引用方法二
cout<<obj1.a<<endl;
cout<<obj2.a<<endl;
cout<<Static_Myclass::a<<endl;
return 0;
}
【实例5】使用静态成员函数。
#include <iostream>
using namespace std;
class Myclass
{
private :
static int b;
public :
int a;
static void fun()
{
//a=5; //静态成员函数不能直接使用类内的一般数据成员a
int a = 5; //这是在该函数体内的局部变量
b = 7; //可直接使用类的静态数据成员b
cout<<"a="<<a<<" the class static member b="<<b<<endl;
}
};
int Myclass::b; //静态数据成员的初始化
int main()
{
Myclass::fun(); //不必通过对象操作静态成员函数即可使用
Myclass obj;
obj.a = 30;
obj.fun(); //通过对象操作静态成员函数
return 0;
}
【实例6】使用友元函数。
#include <iostream>
#include <math.h>
using namespace std;
class Point
{
public:
Point(double i,double j)
{
x = i;
y = j;
}
void Getxy()
{
cout<<"("<<x<<","<<y<<")"<<endl;
}
friend double Distance(Point &a,Point &b);
private:
double x,y;
};
double Distance(Point &a,Point &b)
{
double dx = a.x - b.x;
double dy = a.y - b.y;
return sqrt(dx*dx + dy * dy);
}
int main()
{
Point p1(3.0,4.0),p2(6.0,8.0);
p1.Getxy();
p2.Getxy();
double d = Distance(p1,p2);
cout<<"Distance:"<<d<<endl;
return 0;
}
思考与练习
7:
#include"Lab5_1.h"
int main()
{
Student student1,student2;
student1.input_data(01,"lilei","man",99);
student2.input_data(01,"hanmeimei","woman",99);
student1.print_data();
student1.print_data();
return 0;
}
8:
#include"Lab5_1.h"
int main()
{
Student student1,student2;
student1.input_data(01,"lilei","man",99);
student2.input_data(01,"hanmeimei","woman",99);
student1.print_data();
student1.print_data();
return 0;
}
c. 实验六
Main.cpp
#include"computer.h"
int main()
{
COMPUTER computer1;
}
Computer.cpp
#include"computer.h"
void CPU::cpu_run()
{
cout << "frequency:" << CPU_frequency << "voltage " << CPU_voltage << "rank " <<p1 << endl;
}
void CPU:: cpu_stop()
{
CPU::~CPU();
}
CPU::~CPU()
{
cout<<"cpu stop working"<< endl;
}
Computer.h
#ifndef _COMPUTER_H
#define _COMPUTER_H
#include<iostream>
using namespace std;
class CPU
{
public:
void cpu_run();
void cpu_stop();
~CPU();
private:
int CPU_frequency;
float CPU_voltage;
enum CPU_Rank
{
p1 = 1,
p2,
p3,
p4,
p5,
p6,
p7
};
};
class RAM
{
public:
void ram_run();
void ram_stop();
private:
int ram_capacity;
};
class CDROM
{
public:
void cdrom_run();
void cdrom_stop();
private:
int cdrom_capacity;
};
class COMPUTER
{
public:
void computer_run();
void computer_stop();
private:
CPU cpu;
RAM ram;
CDROM cdrom;
};
#include "computer.cpp"
#endif