继承派生(只考虑public继承)以及多态

  • Post author:
  • Post category:其他



一、继承和派生

class B:public A
{
};

B继承自A, B是A的派生。派生类拥有基类的全部成员函数和成员变量,但基类的private成员,派生类无法访问(因此可以将派生类定义成基类的友元类,

友元关系不具有传递性和继承性

),基类的protected成员:派生类的成员函数可以访问当前对象的基类的保护成员。保护成员就是为了继承而生的,其它访问限制与private一样。

关于构造函数问题见写在构造函数一章


继承与成员对象的区别:


无论是B继承A,还是在B中定义一个A类的对象,都可以实现在B类对象的内存里,放一个A类的对象(二者几乎可以达到一样的功能,但无法通过成员对象访问A的保护成员,因为protected为继承而生)。

但它们的应用场景,在逻辑上完全不同。


B继承A的正确场景是

:B类对象从逻辑上也是A类对象。比如男人类可以继承自人类,一个男人也必然是一个人(public继承是这样,其它两种继承不是这种逻辑)。


B中定义成员对象(A)的正确场景是

:A是B的组成部分,比如圆由圆心(点)和半径组成,在定义圆类时就可以定义一个double类型的半径,和点类型的成员变量圆心。点类就是A类。

基类与成员对象的区别不是从类内内容上来区分的,而是从逻辑大小上来区分的。


派生类覆盖基类成员:


派生类中如果定义了和基类同名的成员变量、成员函数。那么默认访问的就是派生类自己定义的同名成员,覆盖了基类的成员。此时要想访问基类中的同名成员,就要通过 基类名::成员名;来访问,但这种访问方法也仅局限在成员函数中访问成员对象作用的对象的基类同名成员;无法访问同类对象的基类的同名成员。

一般来说派生类中不定义基类的同名成员变量(如果非要用基类的私有变量,那就在基类中把它定义成保护成员变量啊)

派生类中经常要定义基类的同名成员函数,这是下面的多态问题。


public继承的兼容性:


1、派生类对象可以赋值给基类对象(只取重合那块)

2、基类的引用可以被派生类对象初始化

3、基类的指针可以指向派生类的对象


二、多态和虚函数


前面提到,派生类中可以定义和基类成员函数同名的成员函数。这种方法可以大大扩充代码的灵活性,因为派生类总有自己的特点,所以我们需要定义同名函数。

基类的指针可以指向派生类对象,那在往函数里传参数时,只要形参是基类指针(或引用),那所有派生类对象岂不是都能传进函数里去。这也太好了吧,本来要重载函数才能传其它类型对象(派生类之间也是不同的类型),这样写一个就行了。

细心一点就会发现,虽然基类指针可以指向派生类的对象,但只能通过该指针访问派生类对象里的基类那部分成员,无法访问新定义的成员(同名也不行),这大大限制了上面说的那种好处。多态和虚函数就是为破解这个限制而设计的。


虚函数:


定义成员函数时,在函数声明前加上关键字virtual,就定义了一个虚函数。如果虚函数的函数体写在外面,实现的时候就不要再写virtual,基类中如果定义了虚函数,那么在派生类中的同名同参的函数不写virtual也是虚函数。如果在派生类中没有继续定义虚函数,那么继承来的那个虚函数也是派生类的虚函数。

虚函数不要定义成私有成员函数,因为影响在其它函数中调用声明为公有的派生类的虚函数(会报错)


多态:


通过基类指针调用基类和派生类中的同名虚函数时:

(1)若该指针指向一个基类的对象,那么被调用是 基类的虚函数;

(2)若该指针指向一个派生类的对象,那么被调用 的是派生类的虚函数。

这种机制就叫做“多态”。

多态的意义是,一方面可以减少函数重载(大家都用基类指针),另一方面是派生类自己的虚函数也是成员函数,可以访问新设的成员变量,这就使基类指针可以通过虚函数访问派生类中新增加的变量。

如果派生类中定义了一个虚函数,而基类中并没有对应的虚函数,那么通过基类指针无法访问这个虚函数。


虚析构函数:


如果基类指针指向派生类的对象,那么在delete指针时,只会调用基类的析构函数,无法摧毁派生类对象。为此我们把基类和派生类的对象都定义成虚函数,那么在delete指针时就可以先执行基类构造函数,再调用派生类的构造函数。

注意:如果一个类准备作为基类,最好把它的析构函数写成虚函数,成员变量定义成protected;

在普通成员函数中调用虚函数是多态,在构造函数和析构函数中调用虚函数,不是多态。编译时即可确定,调用的函数是自己的类或基类中定义的函数,不会等到运行时才决定调用自己的还是派生类的函数。


多态的实现原理:


含有虚函数的类都有一个虚函数表(应该是存放在全局变量区),对象里会有虚函数指针。用sizeof求内存时会因为虚函数指针而多四个字节(sizeof(类名)也是多4个字节,因此类里多的也是虚函数表指针)

虚函数表指针存放在对象内存最前面,派生类的虚函数表指针可被基类指针访问,因此当基类指针指向的是派生类的对象时,基类指针可以利用虚函数表指针找到属于派生类的虚函数(按常理这是超过了基类指针的能力范围),并执行。


纯虚函数和抽象类


纯虚函数就是没有函数体的虚函数

virtual void Print( )  = 0 ;   //纯虚函数 

含有纯虚函数的类叫抽象类。抽象类不能实例化对象,只有它的派生类实现了

所有的

纯虚函数,派生类才能实例化对象。



版权声明:本文为qq_45100518原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。