1、C+程序设计基础主编 杨长兴中国水利水电出版社第第7 7章章 类与对象类与对象7.1 7.1 从面向过程到面向对象从面向过程到面向对象7.2 7.2 类与对象的定义类与对象的定义7.3 7.3 对象的初始化对象的初始化7.4 7.4 对象数组与对象指针对象数组与对象指针7.5 7.5 静态成员静态成员7.6 7.6 友元友元7.7 7.7 常对象和常成员常对象和常成员7.8 7.8 程序实例程序实例7.1 7.1 从面向过程到面向对象从面向过程到面向对象7.1.1 7.1.1 面向对象程序设计的基本概念面向对象程序设计的基本概念1对象与方法对象与方法对象是指现实世界中具体存在的实体。每一个对象
2、是指现实世界中具体存在的实体。每一个对象都有自己的属性(包括自己特有的属性和对象都有自己的属性(包括自己特有的属性和同类对象的共同属性)。属性反映对象自身状同类对象的共同属性)。属性反映对象自身状态变化,表现为当前的属性值。态变化,表现为当前的属性值。方法是用来描述对象动态特征的一个操作序列。方法是用来描述对象动态特征的一个操作序列。消息是用来请求对象执行某一操作或回答某些消息是用来请求对象执行某一操作或回答某些信息的要求。实际上是一个对象对另一个对象信息的要求。实际上是一个对象对另一个对象的调用。的调用。2类类类是具有相同属性和方法的一组对象的集合,类是具有相同属性和方法的一组对象的集合,它
3、为属于该类的全部对象提供了统一的抽象描它为属于该类的全部对象提供了统一的抽象描述。将相似的对象分组形成一个类,每个这样述。将相似的对象分组形成一个类,每个这样的对象被称为类的一个实例,一个类中的所有的对象被称为类的一个实例,一个类中的所有对象共享一个公共的定义,尽管它们对属性所对象共享一个公共的定义,尽管它们对属性所赋予的值不同。赋予的值不同。3封装封装封装(封装(Encapsulation)是指把对象属性和操)是指把对象属性和操作结合在一起,构成独立的单元,它的内部作结合在一起,构成独立的单元,它的内部信息对外界是隐蔽的,不允许外界直接存取信息对外界是隐蔽的,不允许外界直接存取对象的属性,只
4、能通过有限的接口与对象发对象的属性,只能通过有限的接口与对象发生联系。生联系。4继承继承继承(继承(Inheritance)反映的是类与类之间抽象级)反映的是类与类之间抽象级别的不同,根据继承与被继承的关系,可分为基别的不同,根据继承与被继承的关系,可分为基类和衍类,基类也称为父类,衍类也称为子类。类和衍类,基类也称为父类,衍类也称为子类。子类将从父类那里获得所有的属性和方法,并且子类将从父类那里获得所有的属性和方法,并且可以对这些获得的属性和方法加以改造,使之具可以对这些获得的属性和方法加以改造,使之具有自己的特点。一个父类可以派生出若干子类,有自己的特点。一个父类可以派生出若干子类,每个子
5、类都可以通过继承和改造获得自己的一套每个子类都可以通过继承和改造获得自己的一套属性和方法,由此,父类表现出的是共性和一般属性和方法,由此,父类表现出的是共性和一般性,子类表现出的是个性和特性,父类的抽象级性,子类表现出的是个性和特性,父类的抽象级别高于子类。继承具有传递性。继承使得程序设别高于子类。继承具有传递性。继承使得程序设计人员可以在已有的类的基础上定义和实现新类,计人员可以在已有的类的基础上定义和实现新类,所以有效地支持了软件构件的复用。所以有效地支持了软件构件的复用。5多态性多态性不同的对象收到相同的消息产生不同的动作,不同的对象收到相同的消息产生不同的动作,这种功能称为多态性(这种
6、功能称为多态性(Polymorphism)。将)。将多态的概念应用于面向对象程序设计,增强了多态的概念应用于面向对象程序设计,增强了程序对客观世界的模拟性,使得对象程序具有程序对客观世界的模拟性,使得对象程序具有了更好的可读性,更易于理解,而且显著提高了更好的可读性,更易于理解,而且显著提高了软件的可复用性和可扩充性。了软件的可复用性和可扩充性。6 6重载重载在面向对象程序设计中,有两种重载在面向对象程序设计中,有两种重载(OverloadingOverloading):一种是函数重载,另一):一种是函数重载,另一种是运算符重载。函数重载是指同一作用种是运算符重载。函数重载是指同一作用域内若干
7、个参数特征不同的函数可以使用域内若干个参数特征不同的函数可以使用同一个函数名。运算符重载是指同一运算同一个函数名。运算符重载是指同一运算符可以实施于不同类型的操作数上。符可以实施于不同类型的操作数上。7.1.2 C+7.1.2 C+面向对象程序的结构面向对象程序的结构一个面向对象的一个面向对象的C+程序一般由类的声明和类程序一般由类的声明和类的使用两部分组成。类的使用部分一般由主函的使用两部分组成。类的使用部分一般由主函数和有关子函数组成。数和有关子函数组成。以下是一个典型的以下是一个典型的C+程序结构。程序结构。#include using namespace std;/类的定义部分类的定义
8、部分class C int x,y,z;/类类C的数据成员声明的数据成员声明f();/类类C的成员函数声明的成员函数声明;/类的使用部分类的使用部分void main()C a;/建立一个类建立一个类C的对象的对象aa.f();/给对象给对象a发消息,调用成员函数发消息,调用成员函数f()在在C+程序中,程序设计始终围绕程序中,程序设计始终围绕“类类”展开。展开。通过声明类,构建了程序所要完成的功能,体通过声明类,构建了程序所要完成的功能,体现了面向对象程序设计的思想。下面看一个具现了面向对象程序设计的思想。下面看一个具体的例子,直观地了解一下面向对象程序设计体的例子,直观地了解一下面向对象程
9、序设计方法与结构化程序设计方法的区别。方法与结构化程序设计方法的区别。【例例7.1】类的应用示例。类的应用示例。/*ex7_1.cpp*#include using namespace std;class number /定义一个number类 public:void set(int m,int n)/定义设置数据成员值的函数 a=m;b=n;void print()/定义求和并输出的函数 int i,sum=0;for(i=a;i=b;i+)sum=sum+i;coutSum=sumendl;private:int a,b;/定义私有数据成员;void main()number ob;/建立
10、一个类number的对象ob ob.set(10,50);/调用set函数设置数据成员a和b值,分别为10和50 ob.print();/调用print函数求和并输出7.2 7.2 类的定义类的定义7.2.1 7.2.1 类的定义类的定义在在C+中,一个类指定一个独立的对象集合,中,一个类指定一个独立的对象集合,该对象集合由组成该类的对象以及这些对象所该对象集合由组成该类的对象以及这些对象所允许的操作组成。允许的操作组成。1类的定义形式类的定义形式类定义的一般形式如下:类定义的一般形式如下:class 类名类名 public:数据成员或成员函数的定义数据成员或成员函数的定义private:数据
11、成员或成员函数的定义数据成员或成员函数的定义protected:数据成员或成员函数的定义数据成员或成员函数的定义;2类成员函数的定义类成员函数的定义对类的成员函数的定义通常有两种形式,一种对类的成员函数的定义通常有两种形式,一种是在类的定义中直接定义函数,一种是在类外是在类的定义中直接定义函数,一种是在类外定义。前面的例定义。前面的例6.1就是在类内部实现成员函就是在类内部实现成员函数,下面再看一个例子。数,下面再看一个例子。【例例7.2】已知已知f(n)122334n(n1),求,求yf(22)/f(10)的值。的值。/*ex7_2.cpp*#include using namespace
12、std;class calculate public:float f(int n)/求f(n)的成员函数 int i;float sum=0;for(i=1;i=n;i+)sum=sum+i*(i+1);return sum;void print()float y=0.0;y=f(22)/f(10);coutyendl;void main()calculate ob;ob.print();按照类的定义形式,可以在类定义中只给出成按照类的定义形式,可以在类定义中只给出成员函数的原型,而在类外部定义具体的成员函员函数的原型,而在类外部定义具体的成员函数。这种成员函数在类外定义的一般形式如下:数。这
13、种成员函数在类外定义的一般形式如下:函数返回值的类型函数返回值的类型 类名类名:函数名函数名(形参表形参表)(函数体)(函数体)其中双冒号其中双冒号:是作用域运算符,它指出该函数是作用域运算符,它指出该函数是属于哪一个类的成员函数。是属于哪一个类的成员函数。7.2.2 7.2.2 类成员的访问控制类成员的访问控制1.private1.private类成员类成员 一个类中的私有(一个类中的私有(privateprivate)成员(包括数据成员和成)成员(包括数据成员和成员函数)只能被它们所在类的成员函数和友元函数访问,员函数)只能被它们所在类的成员函数和友元函数访问,在在C+C+中类成员存取说明
14、符缺省的情况下的存取特征都中类成员存取说明符缺省的情况下的存取特征都是私有的(是私有的(privateprivate)。)。【例【例7.37.3】类私有成员的定义和访问。类私有成员的定义和访问。/*ex7_3.cpp*#include using namespace std;class Person/Person类的三个私有成员char name10;/姓名private:int age;/年龄 char sex;/性别;main()Person P1;P1.age=20;/非法,不能设置私有类数据成员age的值coutP1.nameendl;/非法,不能读取私有类数据成员name的值 ret
15、urn 0;2.public2.public类成员类成员 一个类中的公有一个类中的公有(public)(public)成员(包括数据成员和成员成员(包括数据成员和成员函数)可以被程序中任何代码(包括函数)访问,一函数)可以被程序中任何代码(包括函数)访问,一般情况下,应尽量将类的数据成员声明为私有般情况下,应尽量将类的数据成员声明为私有(privateprivate),然后为需要被类外部访问的数据成员提),然后为需要被类外部访问的数据成员提供公有的成员函数,实现对私有成员的设置和访问,这供公有的成员函数,实现对私有成员的设置和访问,这种结构能够向类的客户很好的隐藏实现方法,在种结构能够向类的客
16、户很好的隐藏实现方法,在有效减少错误的同时,可以增强程序的可修改性。有效减少错误的同时,可以增强程序的可修改性。3.protected3.protected类成员类成员 使用使用protectedprotected声明的称为保护成员。任何一个类的保护声明的称为保护成员。任何一个类的保护成员仅可以被其自己和派生类的所有非静态成员函数和成员仅可以被其自己和派生类的所有非静态成员函数和友元函数直接访问,也就是说其他的外部函数是不能访友元函数直接访问,也就是说其他的外部函数是不能访问它的。因此,对于那些既要对外界隐藏,又要能被派问它的。因此,对于那些既要对外界隐藏,又要能被派生类访问的成员,可以将它们
17、声明为保护成员。生类访问的成员,可以将它们声明为保护成员。如果基类声明了私有成员,那么任何派生类都是不能访如果基类声明了私有成员,那么任何派生类都是不能访问它们的,若希望在派生类中能访问它们,应当把它们问它们的,若希望在派生类中能访问它们,应当把它们声明为保护成员。如果在一个类中声明了保护成员,就声明为保护成员。如果在一个类中声明了保护成员,就意味着该类可能要用作基类,在它的派生类中会访问这意味着该类可能要用作基类,在它的派生类中会访问这些成员。些成员。如果在类的定义中既不指定如果在类的定义中既不指定private,也不指定,也不指定public,则系统就默认为是私有的。,则系统就默认为是私有
18、的。归纳以上对类类型的声明,可得到其一般形式如下:归纳以上对类类型的声明,可得到其一般形式如下:class 类名类名 private:私有的数据和成员函数私有的数据和成员函数;public:公用的数据和成员函数公用的数据和成员函数;private和和public称为成员访问限定符称为成员访问限定符(member access specifier)。存取说明符存取说明符访问控制规则访问控制规则PrivatePrivate此派生类的非静态成员函数和友元函数可以直接访问此派生类的非静态成员函数和友元函数可以直接访问ProtectedProtected此派生类和其子类非静态成员函数和友元函数可以访此派
19、生类和其子类非静态成员函数和友元函数可以访问问PublicPublic任何非静态成员函数,友元函数和非成员函数都可以任何非静态成员函数,友元函数和非成员函数都可以直接访问直接访问【例7.4】类保护(protected)成员函数的声明。#include using namespace std;class Person/Person类的三个私有成员char name10;/姓名int age;/年龄char sex;/性别/定义保护的成员函数protected:void setage(int newage)/设置年龄 age=newage;int getage()/获取当前年龄 return ag
20、e;/;main()Person P1;cout P1.getage()endl;/非法,类的保护成员不能被外部函数访问 return 0;7.2.3 7.2.3 对象的定义与使用对象的定义与使用1对象的定义对象的定义对象的定义形式如下:对象的定义形式如下:类名类名 对象名表对象名表;其中对象名表代表有多个对象名,各对象名之其中对象名表代表有多个对象名,各对象名之间以逗号分隔。间以逗号分隔。2对象成员引用对象成员引用具体引用形式为:具体引用形式为:对象名对象名.数据成员名数据成员名对象名对象名.成员函数名成员函数名(实参表实参表)【例例7.5】定义一个时钟类,类中有定义一个时钟类,类中有3个私
21、有数据个私有数据成员(成员(Hour、Minute和和Second)和两个公有成员)和两个公有成员函数(函数(SetTime和和ShowTime)。)。SetTime根据传递根据传递的的3个参数为对象设置时间,个参数为对象设置时间,ShowTime负责将对象负责将对象表示的时间显示输出。在主函数中,建立一个时间表示的时间显示输出。在主函数中,建立一个时间类的对象,先利用默认时间设置,再设置时间为类的对象,先利用默认时间设置,再设置时间为10时时23分分45秒并显示该时间。秒并显示该时间。/*ex7_5.cpp*#include using namespace std;class Clock p
22、ublic:void SetTime(int NewH,int NewM,int NewS);void ShowTime();private:int Hour,Minute,Second;void Clock:SetTime(int NewH=0,int NewM=0,int NewS=0)Hour=NewH;Minute=NewM;Second=NewS;void Clock:ShowTime()coutHour时Minute分Second秒endl;void main()Clock MyClock;/定义对象MyClockcout第1次:;MyClock.SetTime();/使用默认值设
23、置时间MyClock.ShowTime();/输出时间cout第2次:;MyClock.SetTime(10,23,45);/设置时间为10:23:45MyClock.ShowTime();/输出时间7.3 7.3 对象的初始化对象的初始化在类的定义中不能给数据成员赋初值。在类的定义中不能给数据成员赋初值。从封装的目的出发,类的数据成员应该多为私从封装的目的出发,类的数据成员应该多为私有的,对私有数据成员的访问只能通过成员函有的,对私有数据成员的访问只能通过成员函数,而不能通过成员引用的方式来赋值。数,而不能通过成员引用的方式来赋值。C+中定义了一种特殊的初始化函数,称之为中定义了一种特殊的初
24、始化函数,称之为构造函数(构造函数(Constructor)。在特定对象使用结)。在特定对象使用结束时,还将进行一些清除工作。对象清除工作束时,还将进行一些清除工作。对象清除工作由析构函数(由析构函数(Destructor)来完成。)来完成。7.3.1 7.3.1 构造函数构造函数1构造函数的特点构造函数的特点(1)构造函数名与类名相同,且没有返回值,不能)构造函数名与类名相同,且没有返回值,不能指定函数类型。指定函数类型。(2)构造函数必须使具有公有属性,但它不能像其)构造函数必须使具有公有属性,但它不能像其它成员函数那样被显式地调用,它是在定义对象的它成员函数那样被显式地调用,它是在定义对
25、象的同时被系统自动调用的。同时被系统自动调用的。(3)构造函数是特殊的成员函数,函数体可以写在)构造函数是特殊的成员函数,函数体可以写在类体内,也可以写在类体外。类体内,也可以写在类体外。(4)构造函数可以重载,即一个类中可以定义多个)构造函数可以重载,即一个类中可以定义多个参数个数或参数类型不同的构造函数。参数个数或参数类型不同的构造函数。【例例7.6】使用构造函数替代例使用构造函数替代例7.5中中SetTime()成员函数,并在主函数中,使用构造函数设置成员函数,并在主函数中,使用构造函数设置时间为时间为15时时19分分56秒并显示该时间。秒并显示该时间。/*ex7_6.cpp*#incl
26、ude using namespace std;class Clockpublic:Clock(int H,int M,int S);/构造函数声明void ShowTime();private:int Hour,Minute,Second;Clock:Clock(int H,int M,int S)/构造函数的实现Hour=H;Minute=M;Second=S;void Clock:ShowTime()coutHour时Minute分Second秒endl;void main()Clock MyClock(15,19,56);/调用构造函数,设置时间为15:19:56MyClock.Sho
27、wTime();/输出时间构造函数也可以重载。关于重载的概念将在第构造函数也可以重载。关于重载的概念将在第7章详细介绍,这里先看一个例子。章详细介绍,这里先看一个例子。【例例7.7】构造函数重载定义示例。构造函数重载定义示例。/*ex7_7.cpp*#include#include using namespace std;class Point int xVal,yVal;public:Point(int x,int y)xVal=x;yVal=y;Point(float,float);Point(void)xVal=yVal=0;void display()coutxValtyValn;Po
28、int:Point(float len,float angle)xVal=(int)(len*cos(angle);yVal=(int)(len*sin(angle);void main()Point obj1(15,70);obj1.display();Point obj2(78.5f,3.14159f);obj2.display();Point obj3;obj3.display();综上所述,构造函数是一个有着特殊名字,在对象综上所述,构造函数是一个有着特殊名字,在对象创建时被自动调用的一种函数,它的功能就是完成创建时被自动调用的一种函数,它的功能就是完成对象的初始化。对象的初始化。2默
29、认的构造函数默认的构造函数如果类定义中没有给出构造函数,则如果类定义中没有给出构造函数,则C+编译器自动给出一编译器自动给出一个默认的构造函数,而且默认的构造函数只能有一个,形式个默认的构造函数,而且默认的构造函数只能有一个,形式如下:如下:类名类名:默认构造函数名默认构造函数名()若没有定义过任何形式的构造函数,系统会自动生成默认的若没有定义过任何形式的构造函数,系统会自动生成默认的构造函数。若已经定义过构造函数,则系统不会自动生成默构造函数。若已经定义过构造函数,则系统不会自动生成默认的构造函数,一旦需要,则要求显式地定义这种形式的构认的构造函数,一旦需要,则要求显式地定义这种形式的构造函
30、数。在程序中,若定义一个静态对象而没有指明初始值造函数。在程序中,若定义一个静态对象而没有指明初始值时,编译器会按默认的构造函数对对象的数据成员初始化为时,编译器会按默认的构造函数对对象的数据成员初始化为0或空。或空。【例例7.8】默认构造函数示例。默认构造函数示例。/*ex7_8.cpp*#include using namespace std;class sampleint i,j;public:void print()coutitjendl;void main()static sample temp;temp.print();【例例7.9】构造函数的调用。构造函数的调用。/*ex7_9.
31、cpp*#include using namespace std;class sample int i,j;public:sample()/显式地定义默认形式的构造函数 sample(int ti,int tj)i=ti;j=tj;void print()coutitjn;void main()static sample t1;/若没有默认形式的构造函数,编译时会出错 sample t2(3,50);t1.print();t2.print();7.3.2 7.3.2 析构函数析构函数1析构函数的特点析构函数的特点当对象创建时,会自动调用构造函数进行初始化。当对象创建时,会自动调用构造函数进行初
32、始化。当对象撤消时,也会自动调用析构函数进行一些清当对象撤消时,也会自动调用析构函数进行一些清理工作,如释放分配给对象的内存空间等。与构造理工作,如释放分配给对象的内存空间等。与构造函数类似的是:析构函数也与类同名,但在名字前函数类似的是:析构函数也与类同名,但在名字前有一个有一个“”符号,析构函数也具有公有属性,也没符号,析构函数也具有公有属性,也没有返回类型和返回值,但析构函数不带参数,不能有返回类型和返回值,但析构函数不带参数,不能重载,所以析构函数只有一个。重载,所以析构函数只有一个。【例例7.10】析构函数程序举例。析构函数程序举例。/*ex7_10.cpp*#include usi
33、ng namespace std;class Pointint a,b;public:Point(int a1,int b1)/构造函数a=a1;b=b1;Point()/析构函数cout调用析构函数endl;void show()couta=a,b=bendl;void main()Point ObjA(31,17);ObjA.show();Point ObjB(7,4);ObjB.show();2默认的析构函数默认的析构函数和默认构造函数一样,如果类定义中没有给出析构函和默认构造函数一样,如果类定义中没有给出析构函数,系统也会自动生成一个默认的析构函数,其格式数,系统也会自动生成一个默认的
34、析构函数,其格式如下:如下:类名称类名称:默认析构函数名默认析构函数名()例如,编译系统为类例如,编译系统为类Point生成默认的析构函数如下:生成默认的析构函数如下:Point:Point()对于大多数类而言,默认的析构函数就能满足要求。对于大多数类而言,默认的析构函数就能满足要求。只有在一个对象完成其操作之前需要做一些内部处理只有在一个对象完成其操作之前需要做一些内部处理时,才显式地定义析构函数。时,才显式地定义析构函数。7.3.3 7.3.3 复制构造函数复制构造函数复制构造函数的作用是使用一个已存在的对象去初始化复制构造函数的作用是使用一个已存在的对象去初始化另一个同类对象,它也是一种
35、构造函数,除了具有一般另一个同类对象,它也是一种构造函数,除了具有一般构造函数的特征外,它还具有如下特点:构造函数的特征外,它还具有如下特点:(1)其形参必须是本类的对象的引用。)其形参必须是本类的对象的引用。(2)某函数的形参是类的对象,调用该函数需要复制)某函数的形参是类的对象,调用该函数需要复制构造函数进行形参和实参结合。构造函数进行形参和实参结合。(3)函数的返值是类的对象,函数调用返回的时候需)函数的返值是类的对象,函数调用返回的时候需要调用复制构造函数实现类对象的赋值。要调用复制构造函数实现类对象的赋值。复制构造函数的定义格式如下:复制构造函数的定义格式如下:类名类名:复制构造函数
36、名复制构造函数名(const 类名类名&对象名对象名)(函数体函数体)复制构造函数与类同名,复制构造函数与类同名,const是类型修饰符,是类型修饰符,被其修饰的对象是个不能被更新的常量。被其修饰的对象是个不能被更新的常量。【例例7.11】默认复制构造函数示例。默认复制构造函数示例。/*ex7_11.cpp*#include using namespace std;struct sampleprivate:int length,width,s;public:sample(int i,int j)length=i;width=j;s=length*width;coutst调用构造函数n;samp
37、le()coutlengthtwidtht;coutst调用析构函数n;void main()sample obj1(15,6);sample obj2(obj1);/建立对象obj2sample obj3=obj1;/建立对象obj3【例例7.12】复制构造函数示例。复制构造函数示例。/*ex7_12.cpp*#include using namespace std;class Point public:Point(int a=0,int b=0)/构造函数 X=a;Y=b;Point(const Point&p);/复制构造函数的声明 int GetX()/其它成员函数 return X;
38、int GetY()return Y;private:int X,Y;Point:Point(const Point&p)/复制构造函数的实现 X=p.X;Y=p.Y;cout复制构造函数被调用endl;void main(void)Point A(10,20);Point B(A);coutB.GetX()tB.GetY()endl;普通构造函数在建立对象时被调用,而复制构造函数在用已普通构造函数在建立对象时被调用,而复制构造函数在用已有对象初始化一个新对象时被调用。复制构造函数被调用通有对象初始化一个新对象时被调用。复制构造函数被调用通常发生在以下常发生在以下3种情况:种情况:(1)程序中
39、需要新建一个对象,并用一个类的对象去初始)程序中需要新建一个对象,并用一个类的对象去初始化类的另一个对象的时候。化类的另一个对象的时候。(2)当对象作函数参数时,调用该函数时需要将实参对象)当对象作函数参数时,调用该函数时需要将实参对象完整地传递给形参,这就需要按实参复制一个形参,系统是完整地传递给形参,这就需要按实参复制一个形参,系统是通过调用复制构造函数来实现的,这样能保证形参具有和实通过调用复制构造函数来实现的,这样能保证形参具有和实参完全相同的值。参完全相同的值。(3)当函数的返回值是类的对象。在函数调用完毕需要将)当函数的返回值是类的对象。在函数调用完毕需要将返回值带回函数调用处时,
40、此时需要将函数中的对象复制一返回值带回函数调用处时,此时需要将函数中的对象复制一个临时对象并传给该函数的调用处。个临时对象并传给该函数的调用处。以上以上3种调用复制构造函数都是由编译系统自动完成的,不种调用复制构造函数都是由编译系统自动完成的,不必由用户自己去调用。必由用户自己去调用。7.4 7.4 对象数组与对象指针对象数组与对象指针7.4.1 7.4.1 对象数组对象数组对象数组是指数组的每一个元素都是相同类型对象的数组,对象数组是指数组的每一个元素都是相同类型对象的数组,也就是说,若一个类有若干个对象,把这一系列的对象用一也就是说,若一个类有若干个对象,把这一系列的对象用一个数组来表示。
41、对象数组的元素是对象,不仅具有数据成员,个数组来表示。对象数组的元素是对象,不仅具有数据成员,而且还有成员函数。而且还有成员函数。对象数组的定义和普通数组的定义类似,一般格式如下:对象数组的定义和普通数组的定义类似,一般格式如下:类名类名 数组名数组名第一维大小第一维大小第二维数组大小第二维数组大小其中,类名是指该数组元素属于该类的对象,方括号内的数其中,类名是指该数组元素属于该类的对象,方括号内的数组大小给出了某一维元素的个数。一维对象数组只有一对方组大小给出了某一维元素的个数。一维对象数组只有一对方括号,二维对象数组要有两个方括号对,等等。括号,二维对象数组要有两个方括号对,等等。与普通数
42、组一样,在使用对象数组时也只能访与普通数组一样,在使用对象数组时也只能访问单个数组元素,也就是一个对象,通过这个问单个数组元素,也就是一个对象,通过这个对象,可以访问它的公有成员,一般形式如下:对象,可以访问它的公有成员,一般形式如下:数组名数组名下标下标.成员名成员名和普通数组一样,对象数组既可以在定义时初和普通数组一样,对象数组既可以在定义时初始化,也可以在定义后赋值。始化,也可以在定义后赋值。【例例7.13】对象数组应用示例。对象数组应用示例。/*ex7_13.cpp*#include using namespace std;class sample int x,y;public:sam
43、ple(int x1,int y1)x=x1;y=y1;void display()coutx=xty=yendl;void main(void)int i;sample array3=sample(10,20),sample(30,40),sample(50,60);for(i=0;i数据成员名数据成员名或:或:(*指向对象的指针指向对象的指针).数据成员名数据成员名引用成员函数的具体形式如下:引用成员函数的具体形式如下:指向对象的指针指向对象的指针-成员函数名成员函数名(实参表实参表)或:或:(*指向对象的指针指向对象的指针).成员函数名成员函数名(实参表实参表)【例例7.14】对象指针应
44、用示例。对象指针应用示例。/*ex7_14.cpp*#include using namespace std;class schoolprivate:char*teacher;char*student;school*ptr;/ptr是指向对象school类对象的指针public:void initialize(void);void output(school*ptr);school Yali;/Yali是类school的对象void school:initialize(void)Yali.ptr=&Yali;/将对象Yali的起始地址赋给ptrYali.ptr-teacher=John Min
45、g;(*Yali.ptr).student=Mary Jasmine;Yali.output(Yali.ptr);void school:output(school*ptr)coutteacher is teacherendl;cout student is student*数据成员指针名数据成员指针名;【例例7.15】指向数据成员指针应用举例。指向数据成员指针应用举例。/*ex7_15.cpp*#include using namespace std;class MyClass public:int a,b;void Display()couta=aendl;coutb=b*p=400;/对
46、对象数据成员b赋值MyA.Display();2指向成员函数的指针指向成员函数的指针指向成员函数的指针定义格式如下:指向成员函数的指针定义格式如下:函数返回值类型函数返回值类型(类名类名:*成员函数指针名成员函数指针名)(参数表参数表);定义成员函数指针后要对其赋值,也就是确定指向类定义成员函数指针后要对其赋值,也就是确定指向类的哪一个成员函数。给指向成员函数指针赋值的一般的哪一个成员函数。给指向成员函数指针赋值的一般格式如下:格式如下:成员函数指针名成员函数指针名=&类名类名:成员函数名成员函数名;调用成员函数指针所指向函数的格式如下:调用成员函数指针所指向函数的格式如下:(对象名对象名.*
47、成员函数指针名成员函数指针名)(实参表实参表)或:或:(对象指针名对象指针名-*成员函数指针名成员函数指针名)(实参表实参表)【例例7.16】指向类成员函数指针应用举例。指向类成员函数指针应用举例。/*ex7_16.cpp*#include using namespace std;class MyClass int a,b;public:void SetData(int x,int y)a=x;b=y;void Display()couta=atb=bendl;void main()void(MyClass:*pfun)(int,int);/指向成员函数的指针MyClass MyA,MyB;p
48、fun=MyClass:SetData;/成员函数指针指向成员函数SetData(MyA.*pfun)(350,450);/通过成员函数的指针调用/成员函数SetData对a和b赋值MyA.Display();MyB.SetData(-350,-450);/通过成员函数名调用/成员函数SetData对a和b赋值MyB.Display();7.4.4 this7.4.4 this指针指针类的每一个成员函数都有一个隐含的常量指针,通类的每一个成员函数都有一个隐含的常量指针,通常称为常称为this指针。指针。this指针的类型就是成员函数所属指针的类型就是成员函数所属类的类型。当调用成员函数时,它被
49、初始化为被调类的类型。当调用成员函数时,它被初始化为被调用函数的对象的地址。用函数的对象的地址。this指针在系统中是隐含地存指针在系统中是隐含地存在的,也可以显式地使用。在的,也可以显式地使用。【例例7.17】this指针应用举例。指针应用举例。需要注意的是,需要注意的是,this指针是一个指针是一个const指针,不能在指针,不能在程序中修改它,而且程序中修改它,而且this指针的作用域仅在一个对象指针的作用域仅在一个对象的内部。的内部。/*ex7_17.cpp*#include using namespace std;class MyClassint a,b;public:MyClass
50、(int i=0,int j=0)a=i;b=j;void copy(MyClass&ab);void Display()couta=a,b=bendl;void MyClass:copy(MyClass&ab)if(this=&ab)return;*this=ab;void main()MyClass s1,s2(15,23);s1.Display();s1.copy(s2);s1.Display();7.5 7.5 静态成员静态成员7.5.1 7.5.1 静态数据成员静态数据成员静态数据成员的定义格式如下:静态数据成员的定义格式如下:static 数据类型数据类型 变量名变量名;静态数据成