(本科)第6章 面向对象程序设计ppt课件.ppt
课程主讲人:第6章面向对象程序设计C+C+语言程序设计语言程序设计中国铁道出版社中国铁道出版社张思民等主编张思民等主编第 6 章 面向对象程序设计 本章内容本章内容n6.1面向对象的基本概念n6.2类与对象n6.3 构造函数和析构函数n6.4 对象指针和静态类成员n6.5 动态内存分配n 6.6 友元n 6.7 继承与派生n 6.8运算符重载n 6.9 虚函数n 6.10程序设计应用实例n 本章小结6.1 面向对象的基本概念6.1 面向对象的基本概念面向对象的基本概念1 1、 对象的基本概念对象的基本概念 对象是系统中用来描述客观事物的一个实体, 它是构成系统的一个基本单位。 现实世界中的对象有两个共同特征:形态和行为。现实世界中的对象有两个共同特征:形态和行为。 n 图图6.2 6.2 软件对象的变量和方法软件对象的变量和方法 2 2、 类的基本概念类的基本概念 类用class作为它的关键字,例如我们要创建一个汽车类,则可表示为:用类创建对象用类创建对象实例化实例化n 当我们要通过汽车类来创建一个轿车对象,并使用它的刹车行为方法时,则要用下面的格式进行实例化:n /创建汽车对象n 汽车轿车;n /引用汽车对象的刹车函数n 轿车. .刹车( ) ;6.2 类与对象 类是面向对象程序设计的基础和核心,也是实现数据抽象的工具。类中的数据具有隐藏性和封装性,类是实现C+许多高级特性的基础。 6.2.1 类的定义1、类定义的一般形式、类定义的一般形式 类由类声明和类体两部分组成,而类体又由数据成员和成员函数组成。类的组成结构与结构体的组成结构比较如图所示。 类的数据成员和成员函数根据其访问权限分为私有、公有和保护型三种属性。类的一般语法形式如下:类的一般语法形式如下: class 类名 private: ; public: ;protected: ; 类定义的各组成部分说明如下:类定义的各组成部分说明如下:(1)类声明 类声明由class和类名构成。class是定义类的关键字,类名是一种标识符,类名的首字符通常大写。(2)类体 一对大括号“ ”之间的内容称为类体,是类的说明部分,用来说明该类的成员。与结构体类型一样,类的定义要以分号“;”结尾。(3)类的成员类的成员包含数据成员和成员函数两部分。类的成员根据访问权限分为三类:私有成员(private)、公有成员(public)保护型成员(protected)。一个具体的类的组成结构示例:一个具体的类的组成结构示例: 【例【例6-1】一个简单类定义的示例。】一个简单类定义的示例。#include using namespace std; class Test private: /私有成员类外不能够直接访问 int s; public: /共有成员类外能够直接访问 void setSum(int x, int y) s=x+y; void print() coutx+y=sendl; ; void main() Test t; t.setSum(3,8); /公有成员可以外部访问 t.print(); /公有成员可以外部访问 ;结构体与类的比较结构体与类的比较 6.2.2 类的成员函数1、在类体之外定义成员函数为了提高类的可读性,常常把成员函数放在类定义体之外,而类体中只保留成员函数的原型声明。在类体之外定义成员函数称为外联函数外联函数。返回类型 类名 :成员函数名参数表/函数体类区分符类区分符【例【例6-2】使用外联函数编写计算】使用外联函数编写计算1 + 2 + 3 + + 100的程序。的程序。#include using namespace std;class Sum /定义求和类 private: /私有数据成员,类外不能够直接访问 int s; public: /共有成员,类外能够访问 void setSum(int x) ; void print(); ; void Sum:setSum(int x) s=0; for(int i=1; i=x; i+) s=s+i; /加法器加法器 void Sum:print() cout1+2+3+100 = sendl; void main() Sum a; a.setSum(100); /公有成员可以外部访问公有成员可以外部访问 a.print(); /公有成员可以外部访问公有成员可以外部访问 2、内联函数、内联函数n 内联函数是指程序在编译时将函数的代码插入在函数的调用处,作为函数体的内部扩展,以避免函数调用机制所带来的开销,提高程序的执行效率。n 内联函数有两种定义方法,一种方法是在类体内定义成员函数,另一种方法是使用inline关键字。6.2.3 对象 类和对象的关系相当于普通数据类型与其变量的关系。类是一种逻辑抽象概念,声明一个类只是定义了一种新的数据类型,对象说明才真正创建了这种数据类型的物理实体。由同一个类创建的各个对象具有完全相同的数据结构,但它们的数据值可能不同。 1、对象的创建、对象的创建定义一个类的对象很简单,与声明普通数据类型的变量相同,其格式如下:其格式如下:类名类名 对象名;对象名;当声明同一个类的多个对象时,多个对象之间用逗号分隔。例如,在【例6-2】的主函数文件 t6-2.cpp中,定义类Sum的对象a为: Sum a; 2、类成员的访问、类成员的访问 对于类成员的访问,如果在该类的内部访问类成员,只要指出它的名字就可以直接使用。如果在类的外部使用类成员,则要通过类的对象来访问其公有成员。一旦创建了一个类的对象,程序就可以用成员运算符“.”来访问类的公有成员,其一般形式为: 对象名公有数据成员名; 或 对象名公有成员函数名(实参表) ;注意:注意: 只有用public定义的公有成员才能使用成员运算符“.”访问,对象中的私有成员是类中隐藏的数据,不允许在类外的程序中被直接访问,只能通过该类的公有成员函数来访问它们。 在【例6-2】中,定义类Sum的对象a,通过对象a完成对成员函数setSum ( )的调用: Sum a; a.setSum(100); 【例【例6-3】创建一个圆面积类,计算圆的面积。】创建一个圆面积类,计算圆的面积。#include using namespace std; #define PI 3.24159 class Circle private: /私有成员不能够直接访问 double radius; public: /共有成员类外能够直接访问 void setRadius(double r); double cirArea(); void Circle : setRadius(double r) radius = r; ; double Circle : cirArea() double area = 0;area = PI * radius * radius;return area; ; void main() double s, r = 3;Circle cir; /定义对象定义对象cir cir.setRadius(r); /公有成员可以外部访问公有成员可以外部访问 s = cir.cirArea(); /公有成员可以外部访问公有成员可以外部访问cout 圆的面积圆的面积 = s endl;3、对象指针访问类成员、对象指针访问类成员n 一个类在声明之初,系统并没有为其分配内存空间,只有当创建了类对象之后,系统才为类对象分配内存空间。因此,可以用一个指针指向对象的首地址。n 如果一个指针指向了一个对象,则可以通过该指针来访问它所指向的对象成员。n 对象指针的成员表示方法如下:对象指针名公有数据成员名; 或 对象指针名公有成员函数名(实参表) ;以下两种表示方法是等价的:对象指针名公有数据成员名;(*对象指针名)公有数据成员名;【例【例6-4】对象指针的应用示例,计算圆的面积。】对象指针的应用示例,计算圆的面积。#include using namespace std; #define PI 3.14class Circle private: double radius; public: double setRadius(int r);double Circle : setRadius(int r) radius = r ; cout 经参数传递,圆的半径设为: radius setRadius(8); cout 圆的面积为: s endl;6.3构造函数和析构函数n C+在类说明中引进了构造函数,构造函数在对象被创建时自动调用,对象所要执行的所有初始化工作都由构造函数自动完成。 n 与构造函数相对应的是析构函数。创建一个对象时,需要给该对象分配内存空间;当这个对象使用完后,就应释放这些空间。析构函数完成当一个对象使用结束时所要进行的清理工作,当一个对象消失时,析构函数被自动调用,释放被对象占用的资源。 构造函数构造函数n构造函数是在类中声明的一种特殊的成员函数,作用是在对象被创建时使用特定的值构造对象,将对象初始化为一个特定的状态。 n构造函数定义规则:构造函数定义规则:构造函数的名字必须与其所属的类名相同,并声明为公有成员函数,且没有任何类型的返回值。构造函数可以访问类的所有数据成员,可以是内联函数,可以带有参数,还可以带默认的形参值。构造函数也可以重载,以提供初始化类对象的不同方法。 n构造函数在创建对象时将被自动调用。【例【例6-5】计算圆的面积,要求使用构造函数。】计算圆的面积,要求使用构造函数。#include using namespace std; #define PI 3.14class Circle private: double radius; /半径半径 public: Circle(); /定义构造函数定义构造函数 double area() ; /定义一般函数定义一般函数;Circle:Circle() radius = 8; cout 构造函数被调用构造函数被调用,圆的半径设为圆的半径设为8 endl; double Circle:area() double s = PI*radius*radius ;return s; void main() Circle cir; /构造函数在声明对象时自动调构造函数在声明对象时自动调用用 double s = cir.area(); cout 圆的面积为:圆的面积为: s endl;【例【例6-6】带参构造函数与不带参数的构造函数的定义和使用示例。】带参构造函数与不带参数的构造函数的定义和使用示例。class Sumprivate: int num;public:Sum(); /构造函数构造函数Sum(int a, int b); /带参数的构造函数带参数的构造函数void prnt(); /成员函数成员函数 ;Sum:Sum()num=15; cout调用默认的构造函数,调用默认的构造函数,num=numendl;Sum:Sum(int a, int b)num=a+b; cout调用带参数构造函数计算调用带参数构造函数计算a+b=numendl;void Sum:prnt() cout调用一般成员函数调用一般成员函数endl;void main()Sum s; Sum(); Sum(3,5); s.prnt();重载构造函数class Tdate public: Tdate(); Tdate(int d); Tdate(int m,int d); Tdate(int m,int d,int y); protected: int month; int day; int year; ; 【例6-7】重载构造函数,下面的日期类同时声明了四个构造函数。Tdate:Tdate() year = 2010; month = 4; day = 15 ;cout year / month / day endl; Tdate:Tdate(int d) year = 2010; month = 4;day = d; cout year / month / day endl; Tdate:Tdate(int m, int d) year=2010; month=m;day=d; cout year / month / day endl; Tdate:Tdate(int m, int d, int y) year = y; month = m;day = d; cout year / month / day endl; void main() Tdate aday; Tdate bday(10); Tdate cday(2, 12); Tdate dday(10, 1, 2010); 6.3.3 析构函数n 析构函数同构造函数一样,也是特殊的类成员函数,它的主要作用是在类对象生命期结束时,清理和释放类对象所占用的系统资源。n 析构函数与所属的类同名,其函数名前加一个逻辑非运算符“”,表示构造函数的逆。一个类中只可以定义一个析构函数。【例【例6-8】析构函数应用示例。】析构函数应用示例。class Sumprivate: int num;public:Sum(); /构造函数构造函数void prnt(); /成员函数成员函数Sum(); /析构函数析构函数 ;Sum:Sum()num=15; cout调用默认的构造函数,调用默认的构造函数,num=numendl;Sum:Sum()cout 析构函数被调用析构函数被调用 endl; void Sum:prnt() cout调用一般成员函数调用一般成员函数endl;void main()Sum s; s.prnt();通过上述例子,可以看到构造函数有如下特点:通过上述例子,可以看到构造函数有如下特点:(1)析构函数与构造函数名字相同,但它前面必须加一个“”号。(2)析构函数不具有返回类型,同时不能有参数,也不能重载,一个类 只能 拥有一个析构函数,这与构造函数不同。(3)析构函数不能显式调用,它在类的生命期结束时会被系统自动调用。(4)C+语言规定,任何一个类都必须有析构函数。如果没有定义析构函数,则系统会提供一个默认的析构函数,该默认的析构函数没有任何具体操作。只要类中提供了一个显式的析构函数,那么系统就不再自动提供默认析构函数。【例【例6-9】定义多个重载的构造函数和析构函数,考察析构函数的调用顺序。】定义多个重载的构造函数和析构函数,考察析构函数的调用顺序。class Tdate public: Tdate(); Tdate(int d); Tdate(int m,int d); Tdate(int m,int d,int y); Tdate(); protected: int month; int day; int year; int x; Tdate:Tdate() x = 1;year = 2010; month = 4; day = 15 ;cout year / month / day endl; Tdate:Tdate(int d) x = 2; year = 2010; month = 4;day = d; cout year / month / day endl; Tdate:Tdate(int m, int d) x = 3;year=2010; month=m;day=d; cout year / month / day endl; Tdate:Tdate(int m, int d, int y) x = 4;year = y; month = m;day = d; cout year / month / day endl; Tdate:Tdate() cout 析构函数d x endl;void main() Tdate d1; Tdate d2(10); Tdate d3(2, 12); Tdate d4(10, 1, 2010); 2010/4/152010/4/102010/2/122010/10/1析构函数d4析构函数d3析构函数d2析构函数d1拷贝构造函数n 拷贝初始化构造函数是一种特殊的构造函数,具有一般构造函数的所有特性,它在创建新的对象时才被调用,但其形参是本类的对象的引用,其作用是用一个存在的对象来初始化另一个正创建的同类的对象,将一个已知对象的数据成员的值拷贝给正在创建的另一个同类的对象。n 拷贝构造函数的特点如下。 (1)该函数名与类名相同,因为它也是一种构造函数, 并且该函数不被指定返回类型。 (2)该函数只有一个参数,并且是对某个对象的引用。 (3)每个类都必须有一个拷贝初始化构造函数。拷贝构造函数的一般形式为:拷贝构造函数的一般形式为:class 类名类名 public: 类名类名(类名类名 &对象名对象名); ;类名类名 :类名:类名(类名类名 &对象名对象名) ; 【例【例6-10】拷贝初始化构造函数应用举例。】拷贝初始化构造函数应用举例。 class TPoint private: int x, y; public: TPoint(int a, int b) x = a; y = b; TPoint(TPoint &p); TPoint() cout析构函数被调用。n; int Xcoord() return x; int Ycoord() return y; ;TPoint:TPoint(TPoint &p) x = 2 * p.x; y = 2 * p.y; cout拷贝构造函数被调用。拷贝构造函数被调用。n; void main() TPoint P1(5, 7); TPoint P2(P1); cout P2 = P2.Xcoord() , P2.Ycoord() ”运算符,一般格式为:一般格式为: 对象指针名 - 类成员名 ;或: (*对象指针名).类成员名;【例【例6-11】处理平面上点的坐标。】处理平面上点的坐标。算法分析:算法分析:设平面上点的坐标为P(x, y),创建一个点的坐标类Point,其中,坐标位置x、y为私有数据成员,设置坐标位置函数set_point()及获取坐标点位置函数get_x()、get_y()为成员函数。我们可以通过对象t设置点的坐标位置,再通过对象指针读取点的坐标数据。class Point private:int x, y;public:void set_point(int x1, int y1); int get_x();int get_y();void Point : set_point(int x1, int y1) x = x1;y = y1;int Point : get_x()return x;int Point : get_y()return y;void main()Point t, *p;p = &t;t.set_point(20, 50);cout 点点x的坐标为:的坐标为: get_x() endl;cout 点点y的坐标为:的坐标为: get_y() ”。 【例【例6-12】编写程序,】编写程序,说明当类的成员变量与该类成员函数的参数同名时,用说明当类的成员变量与该类成员函数的参数同名时,用this指针区分哪一个是成员变量。指针区分哪一个是成员变量。class test int x;public: void set_x(int x) this - x =x; int get_x()return this - x;int main()test t;t.set_x(100);cout t.get_x() score=score; this-id=id; this-name = name; count+; sum+=this-score; void Student: printid() /输出学生的学号及成绩 cout学号: id ; cout 姓名: name ; cout成绩: score endl; void Student: printCA() /输出平均分及学生数 cout平均分: sum / 4 endl; cout学生数: count endl; int Student:count=0; /为静态变量赋初值 int Student:sum=0; void main() Student stu1(1001, 陈 红,89), stu2(1002, 张大山,78), stu3(2001, 赵志勇,97), stu4(2002, 李明全,91); stu1.printid(); /输出第一个学生的学号和成绩 stu2.printid(); stu3.printid(); stu4.printid(); stu1.printCA(); /输出平均分及学生数 学号: 1001 姓名:陈 红 成绩: 89学号: 1002 姓名:张大山 成绩: 78学号: 2001 姓名:赵志勇 成绩: 97学号: 2002 姓名:李明全 成绩: 91平均分: 88学生数: 4 在程序中,静态数据成员count 和sum 是所有对象共享的。当创建第一个对象时,调用构造函数,count值加1后为1,同时 sum 累加第一个对象的成绩,创建第二个对象时再调用构造函数count值再加1后为2,sum 累加第二个对象的成绩,即每创建一个对象,就调用构造函数一次,count 值即自增1,sum 即累加该对象的成绩。原因在于它是共享的静态变量,在函数的本次调用和下次调用之间变量仍然有效,其值可以记忆。如图所示。【例【例6-14】累乘计算:】累乘计算: 1 2 3 n class Product private: int x; static long P; /静态数据成员,存放累乘积 public: Product(int a); void getdata(); ; long Product : P = 1; /静态数据成员初始化Product : Product(int a) /构造函数,完成累乘构造函数,完成累乘 x = a; P *= x; void Product : getdata() cout P = P endl; void main() Product t(1); /定义对象并初始化定义对象并初始化 for(int i = 1; i 10; i+) t = i; cout i = i score=score; this-id=id; this-name = name; count+; sum+=this-score; void Student: printid() /输出学生的学号及成绩 cout学号: id ; cout 姓名: name ; cout成绩: score endl; void Student: printCA() /输出平均分及学生数输出平均分及学生数 cout平均分平均分: sum / 4 endl; cout学生数学生数: count endl; int Student:count=0; /为静态变量赋初值为静态变量赋初值 int Student:sum=0; void main() Student stu1(1001, “陈陈 红红”,89), stu2(1002, “张大山张大山”,78), stu3(2001, 赵志勇赵志勇,97), stu4(2002, 李明全李明全,91); stu1.printid(); /输出第一个学生的学号和成绩输出第一个学生的学号和成绩 stu2.printid(); stu3.printid(); stu4.printid(); stu1.printCA(); /输出平均分及学生数输出平均分及学生数 学号: 1001 姓名:陈 红 成绩: 89学号: 1002 姓名:张大山 成绩: 78学号: 2001 姓名:赵志勇 成绩: 97学号: 2002 姓名:李明全 成绩: 91平均分: 88学生数: 46.5 动态内存分配n在前面,所学习的各种数据类型,如int、double、char、bool等基本数据类型,以及指针、数组、函数等自定义数据类型,在编译时都已经由系统分配好将来程序运行时要占用的内存大小和地址。n为了在程序执行过程中自由调配内存的空间和大小,以更合理的使用内存资源,在C+ 中引入了动态内存分配的概念。动态内存分配就是在程序运行过程中根据实际需要申请适量的内存空间,使用结束后还可以将其占用的内存空间释放。C+ 通过new 运算来实现动态内存分配。1、new 运算创建指针对象为类对象分配内存区域运算创建指针对象为类对象分配内存区域n 运算符 new 的功能是动态分配内存。由于 new 是分配一块内存区域,由其创建的对象是一个指向该区域的地址指针变量。new创建指针对象的基本语法形式如下:类名指针对象名;指针对象名 = new构造函数(参数列表);n 运算符 delete 用来删除由 new 建立的对象,释放指针所指向的内存空间。格式如下:delete指针对象名;这时,该对象的析构函数将被调用。【例【例6-16】设有矩形类动态创建一个矩形类对象。】设有矩形类动态创建一个矩形类对象。算法分析:算法分析: 首先创建一个封装了矩形的高和宽类Rectangle,由于动态创建一个对象需要调用构造函数,因此在创建类时要编写其构造函数。然后,使用 new 操作符动态创建一个对象,给对象动态分配内存,这相当于并返回一个指向该对象的指针。释放对象时使用delete运算符来执行它的析构函数。class Rectangle int width, height; public: Rectangle(int w, int h) width = w; height = h; cout 构造宽 width 高 height 的矩形对象n; Rectangle() cout 销毁矩形对象n; int area() return width * height; ;nvoid main()nnRectangle *p;np = new Rectangle(10, 20); /给矩形类对象分配内存,调用构造函数给矩形类对象分配内存,调用构造函数ncout 矩形面积:矩形面积: area() endl; /调用成员函数调用成员函数 area()n delete p;/释放对象,调用析构函数释放对象,调用析构函数n构造宽4高5的矩形矩形面积:20销毁矩形对象销毁对象时,调用析构函数的结果创建对象时,调用构造函数的结果2、为基本数据类型动态分配内存区域、为基本数据类型动态分配内存区域使用运算符 new 也可以动态的为基本数据类型变量分配内存区域,语法形式如下:语法形式如下:指针变量 = new数据类型(初值列表);释放指针变量时,使用 delete 运算符,其语法形式如下:delete指针变量;【例【例6-17】基本数据类型变量动态分配内存。】基本数据类型变量动态分配内存。nvoid main()nn int *pi;n pi = new int; /分配一块内存区域并赋值n *pi = 3;n cout pi= pi *pi= *pi endl;n float *pf;n pf = new float; n *pf = 3.5f; /在指定的内存区域中赋值n cout pf= pf *pf= *pf endl;n double *pd;n pd = new double;n *pd = 4.2;n cout pd= pd *pd= *pd endl;n char *pc;n pc = new char;/分配一块内存区域n strcpy(pc,ookk);/将字符串复制到pc所指向的区域中n cout pc= pc endl;n delete pi;n delete pf;n delete pd;npi=0013FF78 *pi=3pf=00481A10 *pf=3.5pd=004819D0 *pd=4.2pc=ookk3、为数组对象动态分配内存区域、为数组对象动态分配内存区域n 使用运算符 new 也可以创建数组对象,这时需要给出数组的结构说明。n 用new 运算符动态创建一维数组的语法形式如下:指针变量 = new 对象类型名数组容量;其中,数组容量指出数组元素的个数,动态为数组分配内存时不能指定数组元素的初值。另外需要注意,如果是用 new 建立的数组,用 delete 删除时在指针名前面要加“ ”。【例【例6-18】动态创建数组对象】动态创建数组对象n class Rectanglen n int width, height;n public:n Rectangle()n n cout 构造矩形对象n; n n Rectangle()n n cout 销毁矩形对象: width 和 height endl;n n void set(int w, int h) /设置函数n nwidth = w; height = h;n n int area()n n return width * height;n n ;n void main()n nRectangle *p;n p = new Rectangle3; /语句 1: 动态创建对象数组n p0.set(1, 2);np1.set(3, 4); np2.set(5, 6); /语句 2: 设置矩形的高和宽n for( int i=0; i 3; i+)ncout Area is: pi.area() endl;n delete p; /语句 3: 释放对象数组 n 构造矩形对象构造矩形对象构造矩形对象Area is: 2Area is: 12Area is: 30销毁矩形对象: 5和6销毁矩形对象: 3和4销毁矩形对象: 1和26.6 友元由于类具有封装性和隐蔽性,只有该类的成员函数才能访问类的私有成员,程序中的其他函数无法直接访问类的私有成员。 为了解决类外一般函数或其他类的成员函数与类的私有成员之间进行属性共享的障碍,提高访问效率,这时,可以把这些函数定义为该类的友元函数。除了友元函数外,还有友元类。友元函数和友元类统称为友元。 友元为类的封装隐蔽性的隔离墙开了一扇小窗,外界可以通过小窗窥视类内部的一些属性。友元不是类的成员,但它可以访问类的任何成员。1、友元函数、友元函数友元函数是在类定义中用关键字friend声明的非成员函数。其定义格式如下:其定义格式如下:friend friend 函数类型函数类型 友元函数名友元函数名( (参数表参数表) )/ / 函数体函数体友元函数可以放在类中的任何位置,既可以在public区,也可以在private区,与访问权限无关。【例【例6-19】使用友元函数改变类的私有成员数据值。】使用友元函数改变类的私有成员数据值。n class Count n n private: n int x; n friend void set_x(Count &, int); n public: nCount() x = 0; nvoid print() cout x endl; n ; n void set_x(Count &c,int x) n n c.x=x; n n int main () n nCount counter; n cout数据成员初始值: counter.x = ; n counter.print (); n cout通过友元函数set_x()改变私有成员数值:counter.x = ; n set_x(counter,8); n counter.print(); n return 0; n 友元函数访问类私有成员的情况如图所示:2、友元类、友元类同函数一样,某一个类可以声明为另一个类的友元,这样作为友元的类中的所有成员函数都可以访问另一个类中的私有成员。若类B是类A的友元类,则其声明的格式如下声明的格式如下:class Aclass A friend class B friend class B; ;从友元类的声明可以看,若要把类B声明为类A的友元类,类B必须已经存在。当类B声明为类A的友元类之后,类B的所有成员函数都是类A的友元函数。 【例【例6-20】设计一个学生类和一个教师类,】设计一个学生类和一个教师类, 学生类的数据项有学号、姓名和成绩,教师类能修改并显示学生的成绩。学生类的数据项有学号、姓名和成绩,教师类能修改并显示学生的成绩。算法分析:算法分析: 由于要用教师类来操作学生类中的数据项,因此,把教师类声明为学生类的友元类,教师类的成员函数就可以修改和显示学生类的数据了。 n class Student /定义学生类 n n private: n int id, score; /声明普通数据成员 nchar *name;nfriend class Teacher;n ;n class Teacher /定义教师类n n private: n public: n void set_score(Student &stu, int id, char *name, int x);n void play_score(Student &stu);n ;n void Teacher : set_score(Student &stu, int id, char *name, int x)n n stu.id = id;n stu.name = name;n stu.score = x;n n void Teacher : play_score(Student &stu) /输出学生的学号及成绩 n n cout学号 姓名 成绩 endl; n cout stu.id stu.name stu.score endl;n n void main() n nStudent stu;nTeacher t;nt.set_score(stu, 1001, 陈 红, 89 );nt.play_score(stu);n 学号 姓名 成绩1001 陈 红 896.7 继承与派生 继承就是一个新的类拥有全部被继承类的属性和方法。通过继承,可以从已有的类为基础创建一个新的类,而不需要从零开始设计,这个新类可以从已有类继承其资源、继承其特性。我们把这个新类称为派生类或子类,而已有的类称为基类,也称为超类或父类。 类具有继承性类具有继承性继承 继承就是一个派生类拥有全部基类的成员变量和成员函数。派生类的对象不仅可以调用派生类中定义的公有成员,而且可以调用基类中定义的公有成员。 1、基类和派生类在 C+语言中,继承就是一个新的类拥有全部被继承类的成员变量和成员函数。 这样,新产生的继承类不仅有自己特有的成员变量和成员函数,而且有被继承类的全部成员变量和成员函数。C+语言中把产生新类的被继承类称做基类(或父类),把由基类通过继承方式产生的新类称做派生类(或子类)。从基类产生派生类的方法一般分成两种:从基类产生派生类的方法一般分成两种:如果一个派生类只从一个基类继承产生则称做单继承单继承;如果允许一个派生类从两个或两个以上的基类继承产生则称做多继承多继承。 图6.10 单继承与多继承的示意2、 派生类的定义派生类的定义class 派生类名派生类名 :继承属性:继承属性 基类名基类名 派生类成员变量和成员函数定义派生类成员变量和成员函数定义;其中,继承属性为public、private 或 protected三种不同的继承方式:使用继承属性 public的继承方式称做公有继承;使用继承属性 private 的继承方式称做私有继承;使用继承属性protected 的继承方式称做保护继承。在用继承方法产生派生类时,上述三种继承方式的关键字必须选择一个,也只能选择一个。如果一个继承方式关键字都没有选,则默认继承方式是 private。 【例【例6-21】设已有类】设已有类A,创建一个派生类,创建一个派生类B,以公有继承方式继承类,以公有继承方式继承类A。nclass Ann private:nint x;n public:nvoid set_x(int s) x = s; nint