(十五)继承---父辈的遗产

1、多态性是面向对象的核心,而继承则是实现多态性的基础。


2、由类A产生一个类B,类B是类A的特殊版本。类A称为基类,类B称为派生类,类A是父,类B是子。

       类B自动包含基类A的所有数据成员和所有函数成员(只是有一些限制条件)。即派生类继承了基类的成员。

       由类A派生出类B,类B再派生出类C,则A是B的直接基类,A是C的间接基类。


3、继承与聚合

      派生类对象应代表“有意义的基类对象”。

      种类测试:任何派生类对象都是基类类型的对象,即:派生类应描述基类所表示的对象的一个子集。

                    如:基类汽车中分出轿车、卡车类,那么卡车是汽车中的一个子集。

      没有通过种类测试,则不能使用类的派生机制。比如:汽车类中包括引擎类、传送类等,但引擎不是汽车,这种不能用于派生。

     具有包含的性质的依赖性就称为集合,如果没通过种类测试,但是具有包含性质,则称为“聚合”


4、继承类的声明和定义:

      继承类的声明:应包括基类的头文件。

      继承类的定义(实现):继承类的头文件(不包括基类的头文件)


5、基类的私有数据成员也中派生类的成员,它们在派生类中仍然是私有,但不能在派生类类内直接访问,须由基类派生来的其它成员间接访问。


6、protected和private具有相同效果。类外对象不能直接访问。

      不同处:私有成员继承后,无论哪种继承,都是继承类的私有成员,但不能在继承类直接访问。类外对象更不可能访问

                       protected继承后,是保护或私有,可由继承类类内直接访问,类外(基类或继承类外)不能直接访问。

      因此:基类私有成员对基类总是私有,在派生类中:

                                              一、继承类类内不能直接访问基本的私有成员;二、基类的私有成员仍然是派生类中的私有成员。

                 一句话:基类的私有成员是派生类的私有成员,但在派生类中不能直接访问。


7、公有继承:基类成员的访问状态在继承类中不变,原来是公有派生类仍为公有,原来是protected在派生类中也是protected。

       保护继承:基类中公有和保护在继承类中都变成“保护”访问状态。其性质对下一次的继承有影响区别于私有。

       私有继承:基类中的公有和保护在继承类中都变成"私有“,在派生类类内可直接访问。但基类私有成员在派生类类内不能直接访问。


8、数据成员根据需要调节protected和private

      如果私有成员在继承类中不再需要直接访问,可声明为private

      如果基类私有成员在继承类中需要继承类中的函数成员直接访问进行引用,可声明为protected.

      如果需要在派生类中留有接口,可指定为public


9、特使:using

      尽管有上面明确的访问说明,但有时我们只须要某一个函数的访问性更高可更低。可以在派生类中用using单独说明:

      

class B:private A{
	public:
	using A::volume;
	...............
};

     类B私有继承基本A,本来其所有成员在类B中都是私有,但为了使类A中的某成员volume()变成公有,可以使用using及类名作用域来提高访问性。

     这样volume()就变成了公有成员,在继承类的对象中直接使用。

      注意:1、在继承类中对基类成员使用using声明,必须加上基类名限定;

                   2、在继承类中不能提供函数的返回值和参数,只须函数名和空括号即可。

                   3、不能应用于基类的私有成员,因为它们固定为“继承但不能直接访问”。

                   4、访问状态可以扩大可缩小,比如在继承类的私有成员中用using可以把基类的公有继承的公有成员限制成私有状态。


10、在构造一个继承类对象时,必定首先构造对应的基类对象。

        注意:构造的本质:是产生数据成员,即声明并初始化数据成员,从无到有,且有值。(不是对已有数据进行更改赋值)

                   因此:基类中非私有的数据成员(protected或public)可以在派生类中访问,但是,它们不能在派生类构造函数的初始化列表中初始化。

                   一、构造派生类对象时,首先必须先构造基类,因为先有基类的成员产生,再产生派生类的“特别“的成员,来组成一个整体成为派生类的成员。

                  二、初始化列表很高效,它在调用基类构造函数产生对象或数据之前就要进行处理。

                  如果在派生类的初始列表中来初始,这个时候基本的数据并没产生,如果这个时候产生了,那个基类又产生这样不就矛盾了。

        解决:

               因此不能在初始化列表中进行,而是在派生类的构造函数体中进行赋值,因此这个时候,对象的主要部分已经创建,已经通过自动调用基类的默认

              构造函数创建,因此函数体中就可以进行直接引用了(因此已经存在了)


11、同名的情况:重点!!!

        在没学类之前,同一个作用域同时定义两个变量的数据是不允许的。

                                    同一个作用域同时定义两个不同的函数名是可以的(只要参数不同,即重载)。

                                    同一个作用域一个是同名的变量与另一个同名函数是不允许共存。

        同样在类也是满足上面条件的。

        注意:继承类类内是一个作用域,基类类内是一个作用域,两个作用域是不同的。

        查找名字的时候,从本类开始,再到基类。

        如果没有同名的更好,直接就使用其中一个,不论它是继承类类内,还是基类类内。

        但有同名时,如果仅在继承类内,就判断是否是重载(注意基类继承过来的不能这样去规划),因为派生类会隐藏基类的成员。

            如果强制要提到本作用域,必须使用using声明,即在继承类中使用using   基类::函数名      这样继承类就会把基类这个函数纳入本作用域内。

class Base{
	public:
	void show(int a); 
};
class Derived:public Base{
	public:
	void show(double b);
//	using Base::show;  //强行提入到Derived类的作用域中 
}; 

          如上面,show不会重载,因为重载是在同一个作用域才发生,基类和继承类是不同的作用域,继承类会隐藏基类的成员。

          使用using可将本是基类的作用域,提到派生类中(因此它在两个类的都有作用域),这样,就可从派生类中直接使用并发生重载。


12、多重继承:有多个基类的派生类,称为多重继承,当然只有一个基类的称单一继承。

        多重继承,它把这些基类的特性加在一起,形成一个包含所有基类功能的合成对象,也称“混合编程”。

       定义时,每个基类都在冒号后指定,用逗号隔开。

       注意:

                与单一继承一样,如果省略访问指定符,就使用默认的private。


13、重复的继承:多继承有可能它们的间接基类都是来自一个,因为从每一条继承下来都包括基类这个子对象,就造成了重复。

         注意:重复不是错误,是可以允许的,这时必须限定每个对重复基类的引用,用相应的直接基类或上一个基类来限定它是唯一的来源,就可以使用了。

                   但是,一般情况,我们应该避免基类的重复。       


14、虚基类:为了避免基类的重复,告诉编译器,在所有派生类中,基类应只出现一次,没有重复现象,这样就产生了虚基类。

        使用关键字virtual,把类声明为虚基类。把virtual写下访问限定符的后面。如class  B:public virsual A{  };

         这样,多重继承后的派生类中就只有基类的一个实例,不会重复。

        (1) 一个类可以在一个类族中既被用作虚基类,也被用作非虚基类。

  (2) 在派生类的对象中,同名的虚基类只产生一个虚基类子对象,而某个非虚基类产生各自的子对象。

  (3) 虚基类子对象是由最远派生类的构造函数通过调用虚基类的构造函数进行初始化的。

  (4) 最远派生类是指在继承结构中建立对象时所指定的类。

  (5) 派生类的构造函数的成员初始化列表中必须列出对虚基类构造函数的调用;如果未列出,则表示使用该虚基类的缺省构造函数。

              为什么都要列出呢?因为你都不列出,又面临二义性的问题,最远派生类中是由二个或以上直接或间接基类形成,向上去构造调用时

              就会不清楚选择哪一个基类去构造虚拟基类,为了解决这个二义性,虚基类的派生类都必须对虚基类的构造进行调用,否则就是直接调用

              虚基类的缺少(这一样是要调用,只是调用的缺省虚基类的构造函数)。

  (6) 从虚基类直接或间接派生的派生类中的构造函数的成员初始化列表中都要列出对虚基类构造函数的调用。

              但仅仅用建立对象的最远派生类的构造函数调用虚基类的构造函数,而该派生类的所有基类中列出的对

              虚基类的构造函数的调用在执行中被忽略,从而保证对虚基类子对象只初始化一次

  (7) 在一个成员初始化列表中同时出现对虚基类和非虚基类构造函数的调用时,虚基类的构造函数先于非虚基类的构造函数执行。


15、类类型转换

        派生类型转换为基类总是合法和自动的。因为每个派生类对象都至少包含一个基类对象,即派生类总是基类的一个子集。

        如:Box  aBox;  

               aBox=aCarton;//aCarton为派生类对象,因为派生类对象包含基类部分,Carton部分被切开并舍弃。

        但是上面是不能反过来的:aCarton=aBox;因为基类中不包含派生类的信息,这样转换得到不合理解释,所是错误的。

        注意:在多重继承中,如果没有使用虚基数,派生类转换为基数是错误的,因为此时有多个基类子对象,编译器并不知道

                    选择如一个,这样就具有模糊性,编译器是不允许这种情况发生的。

        注意上面两名代码与这个是不同的:Box aBox=aCarton;

                  首先aCarton隐式转换成Box对象,然后调用拷贝构造函数创建aBox对象。


16、多继承的顺序:class A:public B1,public B3,public B2{   public:

                                              A(int a,int b,int c):B1(a),B2(b),B3(c),meberB1(a),menberB2(b),menber(c){}

                                              .........

                                             private:

                                             B3 menberB3;

                                             B2 menberB2;

                                             B1 menberB1;

                                          };

          A多继承于B1,B2,B3,其构造函数中的初始化顺序与其排序无关,即,构造时,先基类后嵌套成员,初始化基类以类前后声明为准,即

         上面声明的是B1,B3,B2,以这个顺序进行初始化,注意不是构造中的B1,B2,B3。然后再初始化嵌套类成员,是以成员的声明为准,即下

         面私有中的B3,B2,B1,而不是构造中的B1,B2,B3的顺序。

         继承有好几种:

          单继承:只有一个基类的派生 。

           多继承:多个基类同时派生一个派生类的。

          多重派生:一个基类同时派生多个派生类的。

           多层派生:派生类向下层继续派生的。






      

      

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值