继承&派生

1、什么是继承和派生ide

   封装、继承、多态是C++的三个重要的特性。在面向对象的技术中强调软件的可重用性,而继承机制就是用来解决软件的重用问题。在C++中,所谓“继承”就是在一个已经存在的类的基础上创建一个新的类。已经存在的类成为基类或父类,新创建的类称为派生类或子类。函数

   一个类从一个已有的类那里得到已有的特性,这种现象称为类的继承。经过继承,一个新建的子类从父类那里得到父类的特性。从另外一角度说,从已有的类(父类)产生一个新的类(子类),称为类的派生。派生类继承类基的全部数据成员和成员函数,并能够对成员作出必要的调整。一个基类能够派生出多个派生类,每个派生类又能做为基类再派生出新的派生类。所以基类和派生类是相对而言的。类的每一次派生都继承了其基类的基本特征,同时又根据须要作出新的调整。一个派生类只从一个基类派生,这种称为单继承。一个派生类也有从两个或多个基类,这种称为多继承。关于基类和派生类的关系能够理解为:基类是派生类的抽象,派生类是基类的具体化。url


2、派生类的声明方式spa

声明一个单继承的派生类的通常形式:指针

   class 派生类名:继承方式 基类名对象

   {继承

         派生类新增长的成员内存

   };作用域

  其中继承方式有三种:public(公用的),protected(受保护的),private(私有的)。此项若是不写则默认为private(私有的)。it

例:假设已经声明了一个基类Time,在此基础上经过单继承声明一个派生类Date

classDate:publicTime                              //公用继承

{

public:

                voiddisplay()

                {

                                cout << _year << _month << _day << endl;

                }

private:

                int_year;

                int_month;

                int_day;

};


3、派生类的构成

   派生类的成员包括从基类继承过来的成员和本身增长的成员两大部分。可是并非说把基类的成员和派生类增长的成员简单的加在一块儿就成为了派生类。构造一个派生类包括如下3个部分:

一、从基类接受成员

   派生类把基类所有成员(不包括构造和析构函数)接受过来。不能选择接受一部分而舍弃另外一部分,这是不可选择的。所以若是不能合理的选择基类的话,会形成数据的冗余。

二、调整从基类接收的成员

   虽然接受基类成员是不可选择的,可是可与对这些成员作出调整,例如同继承方式改变基类成员在派生类中的访问属性。此外,还能够在派生类中声明一个与基类成员同名的成员,则派生类中的新成员会覆盖基类的同名成员。要注意,若是是成员函数的话,不只要求函数名相同,还要求函数的参数列表也相同。

三、在派生类中增长新的成员

   基类只是提供了最基本的功能,而另有些功能未实现,因此就须要在声明派生类时加入某些具体的功能,造成适用于某一特定应用的派生类。此外,在声明派生类时,通常还有定义适用于派生类的构造函数和析构函数。


4、派生类成员的访问属性

一、基类成员函数只能访问基类成员

二、派生类成员函数能够访问派生类本身增长的成员

三、基类的成员函数不能访问派生类新增长的成员。

四、派生类成员函数在类内或类外访问基类的成员,这种状况比较复杂,它不只取决于基类成员在基类中的访问属性,还要考虑派生类所声明的对基类的继承方式。



spacer.gif

经过上表能够看出,在派生类中有4中不一样的访问属性:

一、公用的,在派生类内和派生类外均可以访问。

二、受保护的,派生类内能够访问,派生类外不能够访问。

三、私有的,只能在派生类内进行访问。

四、不可访问的,或者说不能直接访问的,能够经过基类成员函数进行间接访问。


5、派生类的构造函数

  由于基类的构造函数和析构不能经过继承获得,所以,对继承过来的基类成员的初始化工做也要由派生类的构造函数承担,因此就要在执行派生类的构造函数时,调用基类的构造函数。其通常形式为:

    派生类构造函数名(总参数列表):基类构造函数名(参数列表)

   {派生类中新增长数据成员初始化语句}


   基类成员的初始化要经过初始化列表进行初始化。总参数列表中包含了基类构造函数所须要的参数和对派生类自增的数据成员初始化所须要的参数。而基类构造函数名后面的参数列表中只有参数名而不包括参数类型,由于在这里是调用基类的构造函数,这些参数是实参。

例:

classTime

{

public:

                Time(inthour= 0,intminute= 0,intsec= 0) :_hour(hour)

                                , _minute(minute)

                                , _sec(sec)

                {

                }

private:

                int_hour;

                int_minute;

                int_sec;

};



classDate:publicTime

{

public:

                Date(inthour= 0,intminute= 0,intsec= 0,intyear= 0,intmonth= 0,intday= 0) :Time(hour,minute,sec)                            //调用基类的构造函数对基类成员进行初始化

                {

                                _year =year;

                                _month =month;

                                _day =day;

                }

private:

                int_year;

                int_month;

                int_day;

};

有子对象的派生类,对子对象初始化也是一样的方法。


因此派生类构造函数应该包含3个部分:

一、对基类数据成员初始化

二、对子对象初始化

三、对派生类数据成员初始化


   系统时自动调用基类的构造函数的,将派生类的构造函数写成上面那种方式只是为了给基类的构造函数传参而已,假如不写成上面那种形式,系统仍是会调用基类的默认构造函数的(子对象也是相同的道理)。系统先调用基类的构造函数再执行派生类的构造函数。


6、派生类中的析构函数

   咱们只须要定义派生类中新增成员的构造函数便可。基类的析构函数和子对象的析构函数是系统自动调用的。调用析构函数的顺序与调用构造函数的顺序相反,先执行派生类的析构函数再调用基类的析构函数。


7、多重继承

多重继承:运行一个派生类同时继承多个基类。

一、声明多重继承的方法

  例:若是已经声明了类A、类B、类C,能够声明多重继承的派生类D:

    class D:private A,protected B,public C

    { 类D新增的成员};

  D是多重继承的派生类,它以私有继承方式继承A类,以保护继承的方式继承B类,以公用继承的方式继承C类。


二、多重继承派生类的构造函数

   多重继承派生类的构造函数形式与单继承的构造函数形式基本相同,只是在初始化表中包含多个基类构造函数。如:

    派生类构造函数名(总参数列表):基类1构造函数(参数列表),基类2构造函数(参数列表)...

       {派生类中新增数据成员初始化语句 }; 


三、多重继承引起的问题

多重继承会引起二义性问题,即继承的成员同名,这时引用的话会产生二义性而发生错误。

  例:若是类A和类B中都有数据成员name和成员函数display。若是类C是类A和类B的直接派生类。

  若是在main函数中有:

        C c1;

        c1.name;

        c1.display();

   由于类A和类B都有数据成员name和成员函数display,系统没法判别要访问的是哪一个基类的成员,所以编译出错。要解决      这个问题,能够用基类名来限制做用域:

        c1.A::name;

        c1.A::display();     //这样就表示引用的是类A中的成员。


8、虚基类

假如一个派生类有多个直接基类,而这些直接基类又有一个共同的基类。如图:


spacer.gif

   那么就会出现一个问题,在类E中会保存3份类A的成员,若是人们只须要一份类A的成员,那么这种状况下就会占用较多的内存空间,还增长访问的难度。而在实际中人们每每只须要一份类A的成员。为了解决这个问题,C++中提供了虚基类的方法,使得在继承间接共同基类时只保留一份成员。

如今将类A声明为虚基类:

class A

{...};

class B:virtual public A    //A是B的虚基类

{...};

class C:virtual public A    //A是C的虚基类

{...};

class D:virtual public A    //A是D的虚基类

{...};

注意:虚基类并非在声明基类时声明的,而是在声明派生类时,指定继承方式时声明的。



声明虚基类的通常方法以下:

class 派生类名:virtual 继承方式 基类名


   通过虚基类的声明后,当基类经过多条派生路径被一个派生类继承时,该派生类值继承该派生类一次,即基类成员派生类只保留一次。


一、虚基类的初始化

class A

{

A(int i=0){}

...};


class B:virtual public A    //A是B的虚基类

{

B(int j=2):A(j){}

...};


class C:virtual public A    //A是C的虚基类

{

 C(int k=3):A(k)()

...};


class D:virtual public A    //A是D的虚基类

{

D(int n=4):A(n){}

...};


class E:public B,public C,public D

{

E(int i=0,int j=0,int k=0,int n=0):A(i),B(j),C(k),D(n){}

...};


注意:

   在定义类E的构造函数时,与以往使用的方法有所不一样,因为虚基类在派生类中只有一份数据成员,因此这份数据成员必须由派生类之间给出。规定:在最后的派生类中不只要负责对其之间基类进行初始化,还有负责对虚基类进行初始化。C++编译系统只执行最后的派生类对虚基类的构造函数的调用,而忽略虚基类的其余派生类对虚基类构造函数的调用。


9、基类与派生类的转换

   派生类是继承了基类的数据成员,那么基类对象与派生类对象之间是否存在赋值关系,能否进行类型的转换???

   答案是能够的。基类对象与派生类对象之间 有赋值兼容的关系,因为派生类中包含从基类中继承的成员,所以能够将派生类的值赋给基类对象,在使用基类对象的时候能够用派生类对象替代。可是注意,这种关系是单向的,不可逆的。


具体有如下4个方面:

一、派生类对象能够向基类对象赋值,这种关系是单向的。

二、派生类对象能够代替基类对象向基类对象的引用进行赋值或初始化。

三、若是函数的参数是基类对象的或基类对象的引用,相应的实参能够是派生类的对象。

四、派生类的地址能够赋给指向基类对象的指针。即指向基类的指针能够指向派生类的对象。不过,指向的是派生类对象中从基类继承的部分。

相关文章
相关标签/搜索