面向对象程序设计25954

上传人:xins****2008 文档编号:200125161 上传时间:2023-04-14 格式:DOC 页数:16 大小:149KB
收藏 版权申诉 举报 下载
面向对象程序设计25954_第1页
第1页 / 共16页
面向对象程序设计25954_第2页
第2页 / 共16页
面向对象程序设计25954_第3页
第3页 / 共16页
资源描述:

《面向对象程序设计25954》由会员分享,可在线阅读,更多相关《面向对象程序设计25954(16页珍藏版)》请在装配图网上搜索。

1、一、类与对象C+类是由用户定义的一种数据类型,类是不同成员的集合,包括数据成员和对数据操作的成员函数。类定义是一种封装,可以对成员提供一种保护机制。通过类的名字定义对象,对象也称为类的一个实例,计算机为每个对象分配存储空间。对象的属性用数据成员描述,对象的行为就是成员函数完成的操作。一个类可以定义多个对象,各个对象具有不同的属性(数据成员),但它们具有相同的操作行为(成员函数)。 类的定义定义类的语法是:class 类名 private:私有的数据成员和成员函数protected:保护的数据成员和成员函数public:公有的数据成员和成员函数;在C+中用关键字class定义类,class后面为

2、用户定义的类名,类的属性定义放在花括号内,并以分号结束。一旦定义了类,就可以用类的名字作为类型标识符来定义一个对象。类的成员可以用三种方法封装,包括私有部分、公有部分和保护部分,分别用private、protected和public关键字指定(后面加一个冒号),它们指定了类成员的可见性。这三部分的顺序没有固定要求,也不必同时出现,还可以重复出现。如果未指定成员的可见性,将视其为私有成员。私有的数据成员和成员函数在类外是不能操作的,只能在类内使用。成员函数经常作为公有的,提供了与外界的接口,只有通过这个接口才能实现对私有成员的使用。保护部分的数据成员和成员函数不能在类外使用,但继承的子类可以使用

3、,类的继承机制在后面介绍。类是实现封装的工具,所谓封装就是将类的成员按使用方式分类,有条件地限制对类成员的使用,保护部分的成员构成类的内部状态,公有成员则构成与外界通信的接口,通过公有的成员函数来使用私有的数据成员,从而在C+中实现了封装。下面定义一个有关日期的类date:class dateprivate:int year,month,day;public:void setdate(int y,int m,int d)year=y;month=m;day=d;void setmonth(int d);int getmonth()return month;void display();void

4、 date:display()coutyear” 年 ”month” 月 ”day” 日 ”=1 & m=12)month=m;elsecout”非法月份数据!”endl;month=1;在date类中将三个数据成员year、month和day定义为私有成员,分别表示日期的年、月、日。四个成员函数都是公有成员,其中setdate和getmonth两个成员函数是在类内直接定义的,称为内联函数,而setmonth和display两个成员函数是在类外定义的,称为外联函数。通常把较大规模的成员函数定义为外联函数,外联函数定义的一般形式如下:函数类型 类名字:成员函数名字(参数) 函数体对于外联函数,在

5、类内的声明是必须的。 对象的定义定义的类是一种数据类型,通过类定义对象(或实例)。定义了date类之后,下面将定义一个date类的对象x。date x;计算机为定义的对象分配内存单元,程序中必须通过对象使用其成员,下面通过程序例子说明对象的使用方法。【例1】类与对象#include class dateprivate:int year,month,day;public:void setdate(int y,int m,int d)year=y;month=m;day=d;void setmonth(int d);int getmonth()return month;void display()

6、;void date:display()coutyear” 年 ”month” 月 ”day” 日 ”=1 & m=12)month=m;elsecout”非法月份数据!”endl;month=1;void main()int a;date x;x.setdate(2006,3,15);x.display();a=x.getmonth()+1;x.setmonth(a);x.display();该程序的输出结果是:2006年3月15日2006年4月15日应该注意,在类外不能使用类的私有成员,若在主函数内直接修改月份x.month=4是非法的,除非将month数据成员定义成公有的。另外,必须通过

7、对象使用类的公有成员,直接写setmonth(a)也是非法的,要写成x.setmonth(a)。用date类可以定义多个对象,如:date x,y,z;计算机分别为三个对象分配自己的存储单元,它们的属性状态(数据成员)由各自对象确定,而它们的具有相同的操作行为(成员函数),如果将前面的主函数修改如下:void main()int a;date x,y;x.setdate(2006,3,15);y.setdate(2006,6,20);x.display();y.display();a=x.getmonth()+1;x.setmonth(a);x.display();a=y.getmonth()

8、-1;y.setmonth(a);y.display();该程序的输出结果是:2006年3月15日2006年6月20日2006年4月15日2006年5月20日 this指针用date类可以定义多个对象,如前面程序主函数中定义了x和y两个对象,它们具有不同的属性,作为数据成员的日期值分别保存在自己的存储单元中,但是他们具有相同的操作行为,共同享有成员函数。x.display()和y.display()是不同对象分别调用同一个成员函数,display()是怎样区别为哪一个对象输出数据呢?系统为每个成员函数都自动定义一个名字为this的指针,通过x对象调用成员函数display()时,系统自动将对象

9、x的地址传送给成员函数display()的指针this,这样就可以对不同的对象完成相同的操作。前面的成员函数display()可以改写如下:void date:display()coutyear” 年 ”month” 月 ”day” 日 ”endl;编写程序时一般不需要写this指针,它是默认的。在后面的学习中需要理解this指针的作用。二、构造函数构造函数是与类名相同的特殊成员函数,当定义类的对象时,构造函数被自动调用以实现对象的初始化。构造函数的定义和声明的方法与一般成员函数相同,但构造函数没有返回值问题,不能定义构造函数的类型。构造函数也可以重载,它们具有不同的参数类型或参数个数,下面分

10、别介绍。 缺省构造函数当一个构造函数没有任何参数时,称为缺省构造函数。对于缺省构造函数,只是说它不带任何参数,并不是说不给出它的定义。若类中没有用户定义的构造函数,编译程序自动为其建立一个缺省构造函数(只完成内存分配,对象也没有初始化值)。下面我们看一个缺省构造函数使用的例子。【例2】缺省构造函数#include class dateprivate:int year,month,day;public:date(); /缺省构造函数void display();date:date()year=2006;month=5;day=15;cout”缺省构造函数被调用。n”;void date:disp

11、lay()coutyear” 年 ”month” 月 ”day” 日 ”endl;void main()date x;x.display();程序输出结果是:缺省构造函数被调用。2006年5月15日在主函数内定义x对象时,缺省构造函数date()被自动调用,因此对象的三个数据成员得到了初值,并输出了字符信息。当定义复杂的对象时,使用构造函数初始化对象是非常实用的方法。 带参数的构造函数在定义日期date类对象时,如果不希望得到前面缺省构造函数中提供的日期数据,可以在定义的对象后面直接给出自己希望的日期数据,这需要在类中定义相应的带参数的构造函数。下面程序使用了带参数的构造函数。【例3】带参数的

12、构造函数#include class dateprivate:int year,month,day;public:date(); /缺省构造函数date(int y,int m,int d); /带参数的构造函数void display();date:date()year=2006;month=5;day=15;cout”缺省构造函数被调用。n”;date:date(int y,int m,int d)year=y;month=m;day=d;cout”带参数的构造函数被调用。n”;void date:display()coutyear” 年 ”month” 月 ”day” 日 ”endl;v

13、oid main()date x,y(2006,6,25);x.display();y.display();程序输出结果是:缺省构造函数被调用。带参数的构造函数被调用。2006年5月15日2006年6月25日从程序运行结果可以看出,第一个对象x由缺省构造函数初始化,第二个对象y由带参数的构造函数初始化。可以定义多个带参数的构造函数,但参数的个数或类型不能完全相同。需要说明的是,一旦用户定义了自己的构造函数,系统将不再提供缺省的构造函数。如果在这个程序例子中只定义带参数的构造函数,而没有定义缺省构造函数,并且在主函数定义同样两个对象date x,y(2006,6,25);,那么定义x对象是不允许

14、的,编译时会出现严重错误提示。 拷贝构造函数当构造函数的参数为该类的一个引用时,称它为拷贝构造函数。如果已经有了一个对象,还需要定义另一个同样的新的对象,拷贝构造函数可以完成这个操作。定义拷贝构造函数的定义形式如下:类名: 类名(类名&)当由已存在的同类型的对象创建一个新对象时,系统自动调用拷贝构造函数,并将参数代表的对象逐域拷贝到新建的对象中,新对象与被拷贝对象占有不同的内存空间。若类中没有定义拷贝构造函数,C+可以为类产生一个缺省的拷贝构造函数,但当类的数据成员中含有指针成员时,缺省的拷贝构造函数不能对其进行复制,这时,需要用户为该类定义拷贝构造函数,以完成对指针成员的拷贝。下面是拷贝构造

15、函数应用的例子。【例4】拷贝构造函数#include class dateprivate:int year,month,day;public:date(); /缺省构造函数date(int y,int m,int d); /带参数的构造函数date(date &c); /拷贝构造函数void display();date:date()year=2006;month=5;day=15;cout”缺省构造函数被调用。n”;date:date(date &c)year=c.year;month=c.month;day=c.day;cout”拷贝构造函数被调用。n”;date:date(int y,i

16、nt m,int d)year=y;month=m;day=d;cout”带参数的构造函数被调用。n”;void date:display()coutyear” 年 ”month” 月 ”day” 日 ”endl;void main()date x,y(2006,6,25),z(y);x.display();y.display();z.display();程序输出结果是:缺省构造函数被调用。带参数的构造函数被调用。拷贝构造函数被调用。2006年5月15日2006年6月25日2006年6月25日定义对象z时自动调用拷贝构造函数,对象z就是y的一个拷贝。如果在函数参数中使用对象参数,那么函数调用时

17、参数值传递过程也自动调用拷贝构造函数,如果函数返回值是一个对象,函数返回时也自动调用拷贝构造函数。三、析构函数大家知道,构造函数的功能是完成对象的初始化,如果在构造函数中为对象申请了堆内存或打开了某个文件,那么当对象最后释放时系统无法自动释放占用的堆内存,也不能关闭打开的文件。在类中定义析构函数可以完成对象的释放操作,当定义的对象被释放时自动调用析构函数,析构函数与构造函数的功能正好相反。析构函数的名字是在类名前加上一个字符“”,析构函数也不能定义返回值类型。与构造函数不同的是,析构函数不能含有任何参数,也不能重载,一个类中只允许有一个析构函数。如果类中没有声明析构函数,系统会自动生成一个缺省

18、的析构函数。在下面简单的程序例子中,只需要体会析构函数的定义方法,析构函数被自动调用的时机。【例5】析构函数#include class dateprivate:int year,month,day;public:date(); /缺省构造函数date(int y,int m,int d); /带参数的构造函数date(); /析构函数void display();date:date()year=2006;month=5;day=15;cout”缺省构造函数被调用。n”;date:date()coutyear”/”month”/”day;cout” 析构函数被调用。n”;date:date(i

19、nt y,int m,int d)year=y;month=m;day=d;cout”带参数的构造函数被调用。n”;void date:display()coutyear” 年 ”month” 月 ”day” 日 ”endl;void main()date x,y(2006,6,25);x.display();y.display();程序输出结果是:缺省构造函数被调用。带参数的构造函数被调用。2006年5月15日2006年6月25日2006/6/25 析构函数被调用。2006/5/15 析构函数被调用。在主函数结束前,系统要释放函数中定义的两个对象x和y,因此先后两次自动调用析构函数。要注意调

20、用析构函数的次序与调用构造函数的次序正好相反,最先定义的对象x在最后释放。四、友员函数类的私有成员是不能在类外使用的,这是封装机制的需要。如果有特殊的要求,可以使用友员函数,友员函数必须在类内用关键字friend 声明。友员函数是能访问类的所有成员(包括私有的和保护的)的普通函数,友员函数虽然是在类内声明,但不属于该类的成员,不能按一般成员函数的方法使用。一个函数也可以同时是多个类的友员函数,但不属于任何类。友员函数的定义在类内和类外都可以。 友员函数的用法友员函数不是类的成员,只能通过函数参数使用类的成员,必须在参数表中显示地指明要访问的对象。C+不允许将构造函数、析构函数和后面要介绍的虚函

21、数声明为友员。【例6】友员函数#include class X1;class X2;class X3private:int x;public:X3(int i)x=i;friend int sum(X1 a,X2 b,X3 c);class X1private:int x;public:X1(int i)x=i;friend int sum(X1 a,X2 b,X3 c);class X2private:int x;public:X2(int i)x=i;friend int sum(X1 a,X2 b,X3 c);int sum(X1 a,X2 b,X3 c)return (a.x+b.x+

22、c.x);void main()int s;X1 m(1);X2 n(2);X3 t(3);s=sum(m,n,t);cout”The sum is ”sendl;程序的输出结果是:The sum is 6程序中将函数sum分别声明为类X1、X2和X3的友员函数,实现了对各个类中私有成员x的访问。当一个类声明了友员函数,类外就能够通过友员函数存取该类的私有成员和保护成员,友员函数在一定程度上破坏了私有数据成员的安全性,因此要慎用。 用友员函数重载运算符传统的C语言系统中所提供的各种运算符只适用于基本数据类型,在C+语言中定义的对象不能使用这些运算符,但C+语言里可以重载这些运算符(只有个别几个

23、特殊运算符不能重载,如条件运算符和一些成员运算符等),运算符重载就是赋予运算符多重定义,使重载后的运算符能作用于类的对象。下面结合一个复数类Complex介绍加法和赋值运算符的重载方法。在Complex类中重载运算符后,就可以象原来C语言那样用表达式完成复数的数据运算了。为了更好地理解运算符重载的使用方法,下面先用成员函数实现两个复数的加法运算,然后再逐步过度。下面为Complex(复数)类定义重载运算符函数(只以运算符+为例)。【例7】用成员函数实现复数的加法#include class Complexprivate:double real,imag;public:Complex(doubl

24、e x,double y)real=x;imag=y;Complex()real=0;imag=0;void sum(const Complex &t1,const Complex &t2); /求和成员函数void display();void Complex:sum(const Complex &t1,const Complex &t2)real=t1.real+t2.real;imag=t1.imag+t2.imag;void Complex:display()if(imag0)coutrealimagiendl;elsecoutreal+imagiendl;void main()Com

25、plex c1(0.3,1.5);Complex c2(1.2,-2.8);Complex c3;coutComplex c1=;c1.display();coutComplex c2=;c2.display();c3.sum(c1,c2);coutc1+c2 =;c3.display();程序运行结果为:Complex c1=0.3+1.5iComplex c2=1.2-2.8ic1+c2 =1.5-1.3i在sum成员函数中,t1和t2参数分别得到主函数中c1和c2的值,并通过this指针将计算结果返回到主函数。t1和t2参数前加定义常量的关键字const后,说明两个对象为只读参数,函数中

26、不允许改变只读参数的值,是避免误操作的安全措施,可以缺省。【例8】重载加和赋值两个运算符求两个复数的和#include class Complexprivate:double real,imag;public:Complex(double x,double y)real=x;imag=y;Complex()real=0;imag=0;Complex operator +(const Complex &t); /求和成员函数void operator =(const Complex &t);void display();Complex Complex:operator +(const Compl

27、ex &t)return Complex(real+t.real,imag+t.imag);void Complex:operator =(const Complex &t)real=t.real;imag=t.imag;void Complex:display()if(imag0)coutrealimagiendl;elsecoutreal+imagiendl;void main()Complex c1(0.3,1.5);Complex c2(1.2,-2.8);Complex c3;coutComplex c1=;c1.display();coutComplex c2=;c2.displa

28、y();c3=c1+c2;coutc1+c2 =;c3.display();程序运行结果为:Complex c1=0.3+1.5iComplex c2=1.2-2.8ic1+c2 =1.5-1.3i在本程序中使用加运算符重载方法替代前面求和的成员函数,在形式上是用operator +替代原来成员函数的名字sum。系统处理c1+c2表达式时按c1.operator+(c2)理解,将加号按一个成员函数operator+处理,通过对象c1调用operator +成员函数,c2为函数的实参,而c1的值由系统用this指针自动处理。最后将返回的复数和对象赋给c3对象,仍需要对赋值运算符进行重载,其方法是

29、一致的。下面程序能够完成一个复数与一个实数求和运算#include class Complexprivate:double real,imag;public:Complex(double x)real=x;imag=0;Complex(double x,double y)real=x;imag=y;Complex()real=0;imag=0;Complex operator +(double t); /求和成员函数void operator =(const Complex &t);void display();Complex Complex:operator +(double t)retur

30、n Complex(real+t,imag);void Complex:operator =(const Complex &t)real=t.real;imag=t.imag;void Complex:display()if(imag0)coutrealimagiendl;elsecoutreal+imagiendl;void main()Complex c1(0.3,1.5);Complex c3;coutComplex c1=;c1.display();c3=c1+1.5;coutc1+1.5 =;c3.display();程序输出结果为:Complex c1=0.3+1.5ic1+1.5

31、 =1.8+1.5;该程序实现了复数0.3+1.5i与实数1.5求和,即c2=c1+1.5。如果将c1+1.5改写为1.5+c1,编译会出现错误。如果将加运算符声明为友员,1.5就可以调用加成员运算符了,c2=1.5+c1表达式就可以合法了,下面程序解决这个问题。【例9】将重载运算符声明为友员#include class Complexprivate:double real,imag;public:Complex(double x)real=x;imag=0;Complex(double x,double y)real=x;imag=y;Complex()real=0;imag=0;frien

32、d Complex operator +(const Complex &t1,const Complex &t2); /重载运算符为友员void operator =(const Complex &t);void display();Complex operator +(const Complex &t1,const Complex &t2)return Complex(t1.real+t2.real,t1.imag+t2.imag);void Complex:operator =(const Complex &t)real=t.real;imag=t.imag;void Complex:di

33、splay()if(imag0)coutrealimagiendl;elsecoutreal+imagiendl;void main()Complex c1(0.3,1.5),c2(1.2,1.3);Complex c3,c4;coutComplex c1=;c1.display();coutComplex c2=;c2.display();c3=1.5+c1;cout1.5+c1 =;c3.display();c4=c1+c2;coutc1+c2 =;c4.display();程序运行结果为:Complex c1=0.3+1.5iComplex c2=1.2+1.3i1.5+c1 =1.8+

34、1.5ic1+c2 =1.5+2.8i在本程序例子中要重点理解将加“+”运算符重载函数定义为类的成员时不能完成1.5+c1表达式运算,为什么将重载函数定义为类的友员后就可以计算1.5+c1表达式。另外,例12.15程序中将运算符重载函数定义为类的成员,本程序例子将重载函数定义为友员,两种方法计算c1+c2有何不同,如何传递c1和c2两个对象。当加运算符重载函数为类的成员时,可以将表达式c1+c2理解为c1.operator +(c2),重载的成员函数通过this指针得到c1对象地址,重载函数只用一个引用参数(也可以为指针型参数或对象型参数)使用c2对象。当加运算符重载函数为类的友员时,可以将c

35、1+c2理解为operator +(c1,c2),因为重载函数不是类的成员函数。因为友员函数不是类的成员,因此它可以被非复数类对象调用,可以将1.5+c1理解为operator +(1.5,c1),这时类中必须有带一个实型参数的构造函数完成第一个参数的传递,重载函数的第一个引用型参数也必须用const声明。五、类的静态成员在C+中可以用关键字static定义类的静态成员,包括静态数据成员和静态成员函数。当一个类定义了多个对象时,静态成员只能有一个,为所有对象共享。因此说静态成员属于类,而不属于类的对象。下面介绍静态数据成员和静态成员函数的用法。 静态数据成员用关键字static定义静态数据成员

36、,静态数据成员的初始化必须在全局范围内进行操作。静态数据成员类似于全局变量,静态数据成员是类的一个成员又遵从类成员的访问规则。因此,静态数据成员的使用不但实现了全局变量的功能,而且也保持数据封装的保护功能。【例10】类的静态数据成员#include class Exprivate:static int count;public:Ex()count+;int get()return count;void set(int x)count=x;int Ex:count=0; /在类外全局范围赋初值void main()Ex a1,a2,a3;couta1.count=a1.get()endl;cou

37、ta2.count=a2.get()endl;couta3.count=a3.get()endl;a1.set(5);couta1.count=a1.get()endl;couta2.count=a2.get()endl;couta3.count=a3.get()endl;程序的运行结果是:a1.count=3a2.count=3a3.count=3a1.count=5a2.count=3a3.count=5在Ex类的定义中,count被定义为静态的整型数据成员,静态数据成员count的初始化是在类外完成的,必须在全局范围赋初值,并且要指定所属的类:int Ex:count=0;主函数中共定义

38、了三个对象a1、a2和a3,三个对象的静态数据成员a1.count、a2.count和a3.count共同使用一个存储单元。在类的构造函数中完成count+操作,构造函数被自动调用三次,因此第一次输出的a1.count、a2.count和a3.count的值都等于3。执行语句a1.set(5);后,a1.count=5,因此第二次输出的a1.count、a2.count和a3.count的值都等于5。如果将静态数据成员count定义为公有成员(public),那么a1.set(5);语句可以改为更简单的形式:Ex:count=5;这样,就不必通过对象使用静态数据成员,即使没有定义对象时也可以直

39、接通过类使用其静态数据成员。这也可以看出,在类内定义静态数据成员count只是一种形式上的说明,真正定义是在全局范围完成的。 静态成员函数对于只使用静态数据成员的成员函数,可以将其声明为静态成员函数。例如,可将上例中的成员函数声明如下:static int get();此时成员函数get()就是静态成员函数了。就像对静态数据成员的访问可以不必通过对象一样,对静态成员函数的调用也不必通过对象而直接调用该成员函数。直接调用时,必须指明其所属的类,只与类相联系,而不属于任何对象。六、类的继承与派生软件开发技术最关心的是软件的安全与开发效率,面向对象程序设计方法提供了强有力的支持。类的封装机制可以很好

40、地保护一个对象的数据成员(对象的属性或状态),同时也为控制对象提供了成员函数(对象的行为或操作),将对象的属性与操作封装为一体是一种非常有效的机制。如果实际当中遇到与已经定义的类相似的问题,我们是从头开始重新建立一个类,还是去修改原来已经定义的类,面向对象程序设计技术中的继承机制可以更好地解决这个问题。继承机制可以使用已存在的类定义一个新的类,已存在的类称为基类或父类,新定义的类称为派生类或子类。派生类可以继承基类的成员,体现出代码重用或开发效率。在不破坏基类的情况下,可以为派生类增加新的成员,是一种进一步的再封装。 基类与派生类的继承关系在继承关系中被继承的类为基类(base class),

41、通过继承关系定义的新类称为派生类(derived class)。基类又称父类,而派生类又称子类。派生类不仅可以定义属于自己的新数据成员与成员函数,还可以使用从基类继承来的数据成员与成员函数,同时派生类也可以对一些继承来的函数重新定义,以适应新的要求。定义派生类的一般格式为:class 派生类名字:继承方式 基类名字新的成员;继承方式可以选择public、private和protected三种,它们分别为公有继承、私有继承和保护继承。如果没有指定继承方式,将按私有继承处理。派生类能否继承基类的成员不仅与继承方式有关,而且也与成员的保护属性有关。下面按三种不同的继承方式分别说明: 公有继承公有继承

42、public是最普遍使用的继承方式。在公有继承方式下,派生类不能使用基类的私有成员(不可见),只能使用基类的保护成员和公有成员(可见),并且原来的保护属性不变,基类的保护成员在派生类仍然是被保护的,基类的公有成员在派生类中仍然按公有成员使用。一个类的私有成员和保护成员都不能在类外使用,但保护成员可以被派生类(子类)继承,这是私有成员和保护成员的本质区别。 私有继承在私有继承方式下,派生类仍然不能继承基类的私有成员(不可见),但是可以继承基类保护成员和公有成员,并且在派生类中都按私有成员处理。私有继承方式不常使用。 保护继承在保护继承方式下,不能继承基类的私有成员,可以继承基类的保护成员和公有成

43、员,它们都将为派生类的保护成员。实际上一个派生类也可以作为基类,并继续派生定义新的派生类,这样就构成了一种层次关系。直接从一个基类定义的派生类也称为直接派生类,用新的派生类作基类再次派生定义的类可以说是前一个基类的间接派生类。后面还要介绍用多个类作基类派生一个新类,成为多重继承。只要领会了父子关系(基类与直接派生类的关系),其它继承关系就容易理解了。下面将一个日期类date作为公有继承的基类,派生定义一个日期时间类datetime。/下面是简略的类定义class dateprotected: / protected为保护的int year,month,day; public:void setd

44、ate(int y,int m,int d)year=y;month=m;day=d;void display()coutyear”年”month”月”day”日n”;/下面以日期类date为公有基类,定义一个日期时间派生类datetimeclass datetime:public date protected:int hour,minute,second;/新成员:时(hour)、分(minute)、秒(second)public:void setdatetime(int y,int m,int d,int h,int tm,int s); /新成员,完成年、月、日、时、分、秒的设置void

45、 display(); /重新定义,输出年、月、日、时、分、秒;void datetime:setdatetime(int y,int m,int d,int h,int tm,int s) year=y;month=m;day=d;hour=h;minute=tm;second=s;void datetime:display() coutyear”年”month”月”day”日”hour”时”minute”分” second”秒n”;在上面定义的派生类datetime中,增加了三个新数据成员hour(时)、minute(分)和second(秒),并且自动继承了基类date中的三个数据成员ye

46、ar、month和day。在基类将三个数据成员定义为保护的成员(protected),是为了能够被派生类datetime继承,在公有继承方式下year、month和day三个成员在派生类datetime中仍然具有保护属性。虽然在派生类中可以使用基类的成员函数display(),但它只能输出日期,不能输出时间,因此在派生类中重新定义了,并且新增了一个成员函数settime()完成时间的设置。派生类的数据成员和成员函数有两个来源:一个是从基类继承来的数据成员和成员函数,对于继承来的数据成员,即使无用C+也无权取消,只能不理会它们,但允许对一些继承来的成员函数重新定义;另一个就是由派生类自己定义的数

47、据成员和成员函数,这些成员的定义方法同一般类成员的定义方法是一样的。为了更好地理解基类与派生类之间的继承关系,下面通过一段程序代码说明基类对象和派生类对象如何使用各个成员的方法。【例11】基类与派生类之间的继承关系#include class dateprotected: / protected为保护的int year,month,day; public:void setdate(int y,int m,int d)year=y;month=m;day=d;void display()coutyear”年”month”月”day”日n”;/下面以日期类date为公有基类,定义一个日期时间派生类

48、datetimeclass datetime:public dateprotected:int hour,minute,second;/新成员:时(hour)、分(minute)、秒(second)public:void setdatetime(int y,int m,int d,int h,int tm,int s); /新成员,完成年、月、日、时、分、秒的设置void display(); /重新定义,输出年、月、日、时、分、秒;void datetime:setdatetime(int y,int m,int d,int h,int tm,int s)year=y;month=m;day

49、=d;hour=h;minute=tm;second=s;void datetime:display()coutyear”年”month”月”day”日”hour”时”minute”分” second”秒n”;void main() date x;datetime y;x.setdate(2006,3,15);y.setdatetime(2008,7,12,7,40,26);x.display(); y.display();y.setdate(2008,3,15); /派生类对象使用基类的成员函数y.display();y.date:display(); /派生类对象使用基类的成员函数 该段程

50、序的输出结果为:2006年3月15日2008年7月12日7时40分26秒2008年3月15日7时40分26秒2006年3月15日在任何情况下基类的对象都不能使用属于派生类新增加的数据成员和成员函数。按照继承关系能够继承的所有基类数据成员和成员函数(可见的),都可以被派生类对象使用。如果在派生类中没有重新定义基类的成员函数,派生对象可以直接使用基的成员函数,就象使用自己的成员一样,如:y.setdate(2008,3,15)。如果在派生类中重新定义了基类的成员函数(如display),派生对象y要使用基类定义的成员函数display(),必须用作用域运算符指定基类的名字,如:y.date:dis

51、play(),如果不指定作用域将使用派生类自己的成员函数。如果在派生类成员函数的定义时使用基类的成员函数,也要使用作用域运算符指定基类的名字,如:void datetime:setdatetime(int y,int m,int d,int h,int tm,int s)date:setdate(y,m,d); /使用基类成员函数day=d;hour=h;minute=tm;second=s; 派生类对象的构造和析构在继承关系下派生类可以继承基类中所有非private成员,这里的“成员”是指数据成员和一般成员函数,而对于特殊的构造函数和析构函数无法继承。我们先观察下面程序在定义和释放一个派生类

52、对象时,自动调用基类和派生类中构造函数与析构函数的顺序。【例12】派生类对象的构造与析构顺序#include class date public: date()cout基类date的构造函数被调用!n; date()cout基类date的析构函数被调用!n;class datetime:public date public:datetime()cout派生类datetime的构造函数被调用!n; datetime()cout派生类datetime的析构函数被调用!n;void main() datetime a;程序的输出结果是:基类date的构造函数被调用!派生类datetime的构造函数被

53、调用!派生类datetime的析构函数被调用!基类date的析构函数被调用!从运行结果看出,在定义派生类的对象时,系统首先执行基类的构造函数,然后执行派生类的构造函数。而系统执行析构函数的顺序恰好相反,即先执行派生类的析构函数,再执行基类的析构函数。这是C+语言构造对象的有效机制。在一般情况下创建派生类对象时,由于派生类的对象包含了基类的数据成员,因此派生类的构造函数除了初始化自己定义的数据成员以外,还必须对基类的数据成员进行初始化。虽然派生类的构造函数具有对基类数据成员初始化的权限和能力,最好不使用这种方法。面向对象程序设计方法是在派生类的构造函数中调用基类的构造函数完成基类数据成员的初始化

54、,在派生类构造函数中只初始化自己新定义的数据成员。这样更好地保护了基类数据成员的安全与独立性。派生类的构造函数定义的格式如下:派生类名字:派生类构造函数(形参表):基类构造函数(实参表)-在派生类构造函数的形参表中包括派生类自己的数据成员,也包括调用基类构造函数所需要的实参(基类的数据成员)。构造函数可以重载,定义派生类构造函数时要考虑到不同的参数需求。如果在派生类构造函数中不调用基类的构造函数,系统也会自动调用(前面例子),在派生类构造函数中调用基类的构造函数主要是提供派生对象的初始化参数,在派生类构造函数中没有必要调用基类的缺省构造函数。析构函数不允许使用参数和重载,所以没有必要在定义派生

55、类析构函数中调用基类的析构函数。如果在基类的构造函数中申请了堆内存p1,在派生类的构造函数又申请了堆内存p2,那么需要在派生类析构函数中释放p2,在基类的析构函数中释放p1。最后通过下面程序例子观察派生对象的构造与析构过程。【例13】派生类对象的构造与析构#include class date public: date()coutdate基类析构函数被调用!n; date(int m=3,int d=15,int y=2004);void Setmonth(int m)month=m; void display(); protected: int month,day,year;class da

56、tetime:public date public: datetime(int m=3,int d=15,int y=2004,int h=0,int tm=0,int s=0); datetime()coutdatetme派生类析构函数被调用!n; void display(); protected: int hour,minute,second;void date:display() coutyear年month月day日=1 & m=1 & d=31) month=m;day=d;year=y; else cout日期数据非法!n; cout基类date的构造函数被调用!n;datetime:datetime(int m,int d,int y,int

展开阅读全文
温馨提示:
1: 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
2: 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
3.本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
5. 装配图网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
关于我们 - 网站声明 - 网站地图 - 资源地图 - 友情链接 - 网站客服 - 联系我们

copyright@ 2023-2025  zhuangpeitu.com 装配图网版权所有   联系电话:18123376007

备案号:ICP2024067431-1 川公网安备51140202000466号


本站为文档C2C交易模式,即用户上传的文档直接被用户下载,本站只是中间服务平台,本站所有文档下载所得的收益归上传人(含作者)所有。装配图网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。若文档所含内容侵犯了您的版权或隐私,请立即通知装配图网,我们立即给予删除!