继承和多态一(转)

时间:2022-11-30 16:18:55

第3章 继承与多态

面向对象的三大特征:封装,继承,多态。

3.1 继承

3.1.1 继承的概念

注意: 
1. 基类的构造函数、拷贝构造函数和析构函数不可以被继承。 
2. 派生类继承了基类的全部数据成员和除了构造、析构函数之外的所有成员函数。派生类对于基类成员的继承是没有选择的,不能选择接收或舍弃基类中的某些成员。 
3. 派生类中除了与基类同名的成员外还可以添加新成员,用于实现新功能,保证派生类的功能在基类的基础上的发展。 
4. 多个派生类可以继承自一个基类。 
5. C++中可以通过派生形成类的层次结构,即一个基类可以是另一个更高层次类的派生类,而另一个派生类也可以继续产生派生类。

3.1.2 继承权限

总结出不同情况下的访问权限:

继承和多态一(转)

注意虽然继承的方法分为公有继承、私有继承、保护继承,默认为private(这一点也是class和struct的区别,struct默认是public继承),但不管哪种继承,派生类仅可以访问基类中所有的非私有成员,但我们可以通过基类的公有和保护成员访问它的私有成员,这是封装的重要概念,同时达到了数据抽象的效果。

(1)公有继承:基类的公有也是派生类的公有,基类的保护也是派生类的保护,基类的私有不能继承;

(2)私有继承:基类的公有和保护成为派生类的私有成员;

(3)保护继承:基类的公有和保护成为派生类的保护成员;

3.1.3 派生类操作基类对象的四种方法

1.派生类对象可以向基类对象赋值。(单项的,不可逆) 
2.派生类对象可以替代基类对象向基类对象的引用进行赋值或初始化。

ClassA obj_a; ClassB obj_b; ClassA &refa = obj_a;//可以将这一行改为 ClassA &refa = obj_b; 

3.如果函数的参数是基类对象或基类对象的引用,函数调用时的实参可以是派生类对象。

void func(ClassA &ref) { cout<<ref.num<<endl; }

在调用func()函数时可以用派生类ClassB的对象obj_b作实参:

func(obj_b);

4.派生类对象的地址可以赋值给基类指针变量。

3.2 派生类

3.2.1 构造函数

派生类中定义带参数构造函数的形式如下所示:

派生类名::派生类构造函数名(参数列表):基类构造函数名(基类构造函数参数表)
{ 派生类新增成员的初始化语句 }

3.2.2 析构函数

若派生类中没有显式的定义析构函数,编译系统会提供一个默认的析构函数。显式定义中,对象生命周期结束时会先调用派生类的析构函数,再调用基类的析构函数。

3.2.3 隐藏基类函数

派生类中重新定义基类同名函数的方法,称为对基类函数的覆盖或改写,覆盖后基类同名函数在派生类中被隐藏。定义派生类对象调用该函数时,调用的是自身的函数,基类同名函数不被调用。

3.3 多重继承

3.3.1 声明多重继承的方式

class 派生类名:继承方式 基类1名称,继承方式 基类2名称,... { public: 新增加的公有成员 protected: 新增加的保护成员 private: 新增加的私有成员 }

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

定义:

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

假如派生类是多重继承,并且新增数据成员有一个或多个对象成员,那么派生类需要初始化的数据有三部分:继承的数据成员、新增类的数据成员、新增普通成员。 
这种复杂派生类定义:

派生类名::派生类构造函数名(参数总表):基类1构造函数名(参数表1),基类2构造函数名(参数表2),...子对象名1(参数表n),子对象名2(参数表n+1) { 派生类构造函数体 }

派生类构造函数调用顺序: 
首先,调用基类构造函数。 
其次,调用对象成员的构造函数。 
最后,调用派生类的构造函数。

3.3.3 多重继承引起的二义性

  1. 调用不同基类中的同名成员时产生的二义性 
    消除方法: 
    (1)使用作用域限定符 
    (2)派生类中定义与基类同名函数,将基类函数隐藏
  2. 派生类中访问公有成员时产生二义性

3.3.4 虚基类

在多重继承中,若一个类声明为虚基类,则能保证一个派生类间接地多次继承该类时,派生类中只继承该基类的一份成员,避免了派生类中访问公共基类公有属性多份拷贝的二义性,其效果等同于将基类函数隐藏的方法(上文3.3.3和3.2.3)。 
定义:

class 派生类名:virtual 继承方式 基类名 { 派生类成员 };

3.4 多态

3.4.1 多态性概念

3.4.2 虚函数

虚函数是运行时多态,若某个基类函数声明为虚函数,则其公有派生类将定义与其基类虚函数原型相同的函数,这时,当使用基类指针或基类引用操作派生类对象时,系统会自动用派生类中的同名函数代替基类虚函数。 
声明形式:

class 类名 { virtual 函数返回值类型 函数名(参数表) }
  1. 一般虚函数成员 
    带有虚函数时,C++编译器的操作步骤: 
    (1)为各个类建立虚函数列表,若无虚函数则不操作。 
    (2)暂不连接虚函数,只是将各个虚函数地址放入虚函数表。 
    (3)连接各静态函数。
  2. 虚析构函数

3.4.3 纯虚函数

在定义一个表示抽象概念的类时,有时无法或者不需要给出某些成员函数的具体实现,函数的实现在派生类中完成,基类中这样的函数声明为纯虚函数。 
与虚函数相比,纯虚函数没有函数体,其作用是在基类中为派生类保留一个接口,方便派生类根据需要对它实现,实现多态。 
声明形式:

virtual 函数返回值类型 函数名(参数表)= 0 ; 

3.5 抽象类与内部类

3.5.1 抽象类

如果一个类中至少包含一个纯虚函数,则该类成为抽象类,因此抽象类是基于纯虚函数的。 
定义形式:

class 类名 { public: virtual 函数返回值类型 函数名(参数表)= 0; 其他函数声明 }

3.5.2 内部类

class 外部类名 { 外部类成员; 访问限定符: class 内部类名 { 内部类成员 }; };