第8章 继承.

继承是在一个或多个已存在的类的基础上建立一个新的类,从新类角度说,新类从已有的类那里获得其已有特性,这种现象称为类的继承。通过继承,一个新建子类从已有的父类那里获得父类的特性。从父类角度说,从已有的父类产生一个新的子类,称为类的派生类,而派生类派生类继承了基类的所有数据成员和成员函数***(不包括基类的构造函数和析构函数)***,并可以增加自己的新成员,同时也可以调整来自基类的数据成员和成员函数

类继承关系的语法形式
class 派生类名 : 基类名表
{
数据成员和成员函数声明
};

基类名表 构成
访问控制 基类名1, 访问控制 基类名2 ,… , 访问控制 基类名n
访问控制 表示派生类对基类的继承方式,使用关键字:
public 公有继承
private 私有继承
protected 保护继承

class Base //声明基类
{
public: //基类公有成员
...;
private: //基类私有成员
...;
};
//以public方式声明派生类Derived
class Derived: public Base
{
public:
...;
private:
...;
};

派生类中的成员包括:从基类继承的和自己新加的成员
派生类对基类成员的使用取决于***声明派生类时所指定的对基类的继承方式和基类中成员的访问属性***
在这里插入图片描述
不论以哪种方式继承基类,派生类都不能直接使用基类的私有成员
保护成员的意思是:不能被外界访问,但可以被派生类的成员访问。
基类的部分成员在派生类中可能不需要却也被继承下来,对这些没有实际需要而被继承的成员,在派生类中需要对其进行改造。
1.改造基类成员包括两个方面:
比如:通过私有继承或保护继承, 改变基类成员的访问权限
2.通过在派生类中定义同名成员(包括成员函数和数据成员)来屏蔽(隐藏,hide,隐身)在派生类中不起作用的部分基类成员。
注意,屏蔽不是删除,也就是派生类中继承自基类的成员变量依然存在,而且值也不发生变化。
由于子类是继承父类基础上还增加了自己的部分,所以子类对象空间总是不小于基类对象

  1. 在派生类中,不管基类的数据成员是否可见,都要分配存储空间。
  2. 如果派生类定义的变量名与基类变量名相同,则此变量覆盖掉继承的基类同名变量。如果想引用继承自基类的被覆盖的成员变量c,则要加:: ,
     在成员函数中访问时,直接用base::c,
     用派生类的对象b访问时,如果此继承自基类的成员变量是对象可访问的(public类型),则b.base::c访问
  3. 如果派生类定义的函数名与基类函数名相同同上

1.公有继承

在这里插入图片描述

2.私有继承

在这里插入图片描述

3.保护继承

在这里插入图片描述
从类的用户角度来看,保护成员等价于私有成员。但有一点与私有成员不同,保护成员可以被派生类的成员函数访问。

重名成员

派生类的新成员会屏蔽(hide)与其同名的基类成员,使同名的基类成员成为“不可见”的,即基类成员的名字被隐藏(隐藏不是替代)。
若要在派生类中使用基类的同名成员,显式地使用类名限定符:
类名 :: 成员
在这里插入图片描述
如果是派生类中声明了一个与基类成员函数名字相同,不管参数是否相同,基类中的成员函数也将被隐藏

派生类中访问静态成员

基类定义的静态成员,将被所有派生类共享(基类和派生类共享基类中的静态成员)
派生类中访问静态成员,用以下形式显式说明:

类名 :: 成员

或通过对象访问

对象名 . 成员

基类的初始化

派生类构造函数声明为:

派生类构造函数 ( 变元表 ) : 基类 ( 变元表 ) , 对象成员1( 变元表 ) … 对象成员n ( 变元表 ) ;
构造函数的执行顺序:基类-> 对象成员-> 派生类

派生类构造函数和析构函数的定义规则

  1. 基类的构造函数和析构函数不能被继承
  2. 如果基类***没有定义构造函数***或***有无参的构造函数***或者***带默认形参***,派生类也***可以不用***定义构造函数
  3. 如果基类***无无参的构造函***数,派生类必须定义构造函数
  4. 如果派生类的基类也是派生类,则每个派生类只负责***直接***基类的构造
  5. 派生类是否定义析构函数与所属的基类无关

派生类的析构函数

(1)当派生类中不含对象成员时
•在创建派生类对象时,构造函数的执行顺序是:
基类的构造函数→派生类的构造函数;
•在撤消派生类对象时,析构函数的执行顺序是:
派生类的析构函数→基类的析构函数。
(2)当派生类中含有对象成员时
•在定义派生类对象时,构造函数的执行顺序:
基类的构造函数→对象成员的构造函数→派生类的构造函数;
•在撤消派生类对象时,析构函数的执行顺序:
派生类的析构函数→对象成员的析构函数→基类的析构函数。

class B
{
public:
B( ) { cout<<"B( )"<<endl; }
~B( ) { cout<<"~B( )"<<endl; }
};
class D : public B
{
public:
D( ){ cout<<"D( )"<<endl; }
~D( ) { cout<<"~D( )"<<endl; }
};
int main( )
{
D d;
return 0;
}

结果
B()
D()
~D()
~B()

setioflags(ios::fixed) 固定的浮点显示
setioflags(ios::scientific) 指数表示
setiosflags(ios::left) 左对齐
setiosflags(ios::right) 右对齐
setiosflags(ios::skipws 忽略前导空白
setiosflags(ios::uppercase) 16进制数大写输出
setiosflags(ios::lowercase) 16进制小写输出
setiosflags(ios::showpoint) 强制显示小数点
setiosflags(ios::showpos) 强制显示符号

多继承


多继承是指派生类具有两个或两个以上的直接基类。
多重继承可以看做是单继承的扩展,派生类和每个基类之间的关系可以看做是一个单继承。
多继承声明语法

class 派生类名: 访问控制 基类名1, 访问控制 基类名2,, 访问控制 基类名n 
{
数据成员和成员函数声明
}

其中不同的基类可以选择不同的继承方式。
多个基类的派生类构造函数可以用初始化列表调用基类构造函数初始化数据成员。

派生类名(参数总表):基类名1(参数表1),基类名2(参数表2),,基类名n(参数表n)
{
// 派生类新增成员的初始化语句
}

执行顺序与单继承构造函数情况类似。多个直接基类构造函数的执行顺序取决于定义派生类时指定的各个继承基类的顺序
多继承方式下构造函数的执行顺序:
•先执行所有基类的构造函数
•再执行对象成员的构造函数
•最后执行派生类的构造函数
处于同一层次的各基类构造函数的执行顺序取决于定义派生
类时所指定的基类顺序,与派生类构造函数中所定义的成员初始化列表顺序没有关系,内嵌对象成员的构造函数执行顺序与对象在派生类中声明的
顺序一致

多继承的析构函数

析构函数名同样与类名相同,无返回值、无参数,而且其定义方式与基类中的析构函数的定义方式完全相同。
•功能是在派生类中对新增的有关成员进行必要的清理工作。
•析构函数的执行顺序与多继承方式下构造函数的执行顺序完
全相反:
1.首先对派生类新增的数据成员进行清理,
2.再对派生类对象成员进行清理
3.最后才对基类继承来的成员进行清理。

虚基类

要使这个公共基类在派生类中只产生一个子对象,必须对这个基类声明为虚继承,使这个基类成为虚基类。
1.虚基类用于有共同基类的场合
2. 声明虚基类的一般形式为:

class 派生类名: virtual 继承方式 基类名
例:class B1:virtual public B

作用
1.主要用来解决多继承时可能发生的对同一基类继承多次而产生的二义
性问题。
2.为最远的派生类提供唯一的基类成员,而不重复产生多次拷贝。
注意:
1.虚基类并不是在声明基类时声明的,而是在声明派生类时,指定继承方式时声明的。因为一个基类可以在派生一个派生类时作为虚基类,而在派生另一个派生类时不作为虚基类。
2.在第一级继承时就要将共同基类设计为虚基类。
建立对象时所指定的类称为最(远)派生类。虚基类的成员是由最(远派生类的构造函数通过调用虚基类的构造函数进行初始化的。在整个继承结构中,直接或间接继承虚基类的所有派生类,都必须在构造函数的成员初始化表中给出对虚基类的构造函数的调用。如果未列出,则表示调用该虚基类的默认构造函数。
在建立对象时,只有最(远)派生类的构造函数调用虚基类的构造函数,该派生类的其他基类对虚基类构造函数的调用被忽略。

赋值兼容规则

赋值兼容规则指在程序中需要使用基类对象的任何地方,都可以用公有派生类的对象来替代。
赋值兼容规则中所指的替代包括以下的情况:

  1. 派生类的对象可以赋给基类对象(强制类型转换)
  2. 派生类的对象可以初始化基类的引用
  3. 派生类的对象的地址可以赋给基类类型的指针
    .根据赋值兼容规则, 以下几种情况是合法的:
(1) 可以用派生类对象给基类对象赋值。例如:
Base b;
Derived d;
b=d;
这样赋值的效果是,对象b中所有数据成员都将具有对象d中对应数据成员的值。 
(2) 可以用派生类对象来初始化基类的引用。例如:
Derived d;
Base &br=d;
(3) 可以把派生类对象的地址赋值给指向基类的指针。例如:
Derived d;
Base *bptr=&d;
这种形式的转换,是在实际应用程序中最常见到的。 (4) 可以把指向派生类对象的指针赋值给指向基类对象的指
针。例如:
Derived *dptr,obj; dptr=&obj;
Base *bptr=dptr;

特点:
1.在替代之后,派生类对象就可以作为基类的对象使用,但只能使用从基类继承的成员。
2.一个派生类对象也是一个基类对象,一个基类对象可派上用场的地方,派生类对象一样可派上用场。反之则不然。
赋值兼容应注意的问题
(1)声明为指向基类的指针可以指向它的公有派生类的对象,但
不允许指向它的私有派生类的对象。例如:

class B {}class Dprivate B {};
B b1,*pbl;D d1;
pb1=&b1; //合法,基类B的对象b1和B类的指针
pb1=&d1; //非法,不允许将基类指针指向它的私有派生类对象

(2)允许将一个声明为指向基类的指针指向其公有派生类对象,但是不能将一个声明为指向派生类对象的指针指向其基类的一个对象。
(3) 声明为指向基类对象的指针,当其指向公有派生类对象时,只能用它来直接访问派生类中从基类继承来的成员,而不能直接访问公有派生类的定义的成员。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

jdicat

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值