C++知识复习(三)

引入

  • 继承的核心是为了实现代码复用。面向对象程序设计方法提出了类的继承机制,实现以原有类为基础来创建新类。
    这里我更喜欢从理论上来进行解释。所谓的继承派生不过类上的进一步划分。是高级抽象到次级抽象的转化。比如我们简单定义一个车类,这是一个高级的抽象,为了进一步划分,我们从车的轮胎出发,划分二轮车、四轮车和多轮车的次级抽象,它们都继承了车类的基本属性,如都有轮胎。而四轮车的对象则可以有小汽车、四轮拖拉机。
  • 在C++中,类的继承指在一个已有类(称为基类或父类)的基础上设计新类(称为派生类或子类),新类直接获得已有类的所有所有成员
  • 在继承关系中,C++允许一个基类可以派生多个派生类,而每一个派生类又可以作为基类再继续派生,最高层是抽象程度最高的,是最具有一般意义的概念,它的下层就是由它派生出来的子类,它不仅具有上层的特征,还加入自己的新特征。由上至下是一个由一般到特殊的过程
    在这里插入图片描述

派生类的定义

class 派生类名 : 继承方式  基类名1,继承方式  基类名2, ……
{
		派生类新增成员的定义 ;
}
  • 当需要为某个派生类指定基类时,就在派生类名后面加一个符号“:”,在其后指定基类。除此之外,还需要指定继承方式
  • 继承方式控制着派生类成员以及派生类对象如何访问从基类继承来的成员。在派生类的定义语句中,每一个“继承方式”只能限制紧随其后的一个基类
  • 继承方式可以用3种关键字描述:public、private和protected,分别表示公有继承、私有继承和保护继承

继承方式

派生类继承了基类的全部数据成员和除了构造函数、析构函数之外的全部成员函数,但是派生类能否访问这些成员还要受继承方式的控制
类的继承方式有三种:public、private和protected ,其中private是默认的继承方式

  • 公有继承
    • 当继承方式为public时即表示公有继承
    • 在公有继承方式下,基类的公有和保护成员在派生类中仍然是公有和保护成员,可以由派生类的成员函数来访问,派生类对象可以访问公有的成员;基类的私有成员,派生类的成员函数与派生类对象都无法访问
  • 私有继承
    • 当继承方式为private时即表示私有继承
    • 在私有继承方式下,基类所有的非私有成员,在派生类中一律变成派生类的私有成员,派生类只能通过它的成员函数来访问,派生类对象不能访问;基类的私有成员,在派生类中不可见,只能由基类的成员函数来访问
  • 保护继承
    • 基类的非私有成员,在派生类中都变成保护成员
    • 如果设计一个类的同时还要考虑它将来可能会派生新类,于是在隐藏成员的同时,还要允许派生类的成员函数访问,这时protected 就比private更合适,protected修饰的可被派生类的成员函数访问
      总结来说,无论你采用什么权限继承,基类的私有数据成员对派生类而言都是不可见的,对于保护权限继承,派生类可以访问基类中protected限制的数据成员

派生类的构造函数和析构

派生类不能继承基类的构造函数和析构函数,因此在设计派生类的构造函数时,不仅要考虑派生类新增数据成员的初始化,也要考虑基类数据成员的初始化

构造函数

  • 基类数据成员初始化在派生类的构造函数中都以初始化列表的形式提供,相关语法格式如下:
派生类名(参数表) : 基类名1(参数表1),, 基类名n(参数表n)
{ 
		派生类新增成员的初始化 
}

注意事项

  • 对基类成员和子对象成员的初始化必须在成员初始化列表中进行,新增普通数据成员可以在初始化列表中进行初始化,也可以在构造函数体中进行赋值。
class A			//基类	
{
public:
	A(int x):a(x){}	//或A(int x){a=x;}
private:
	int a;
};
class S			//子对象类
{
public:
	S(int y):s(y){}	//或S(int y){s=y;}
private:
	int s;
};
class B: public A	//派生类
{
public:
	B(int x,int y,int z):A(x),objS(y),b(z){}
		//或B(int x,int y,int z):A(x),objS(y){b=z;}
private:
	S objS;	//子对象成员
	int b;
};
int main(){ B obj(1,2,3); return 0; }
  • 派生类构造函数执行顺序是:先调用基类的构造函数,如果有子对象随后调用子对象的构造函数,最后调用派生类的构造函数。
class A				//基类
{
public:
	A(int x):a(x){ cout << "基类A" << endl; }
private:
	int a;
};
class S				//子对象类
{
public:
	S(int y):s(y){ cout << "子对象类S" << endl; }
private:
	int s;
};
class B: public A		//派生类
{
public:
	B(int x,int y,int z):objS(y),A(x),b(z){cout << "派生类B" <<endl;}
private:
	S objS;	//子对象成员
	int b;
};
int main()
{ 
	B obj(1,2,3); 
	return 0; 
}
  • 有多个基类时,处于同一层次的各个基类的构造函数的调用顺序取决于派生类对基类的声明所指定的顺序(自左向右) 。
class A				//基类
{
public:
	A(int x):a(x){ cout << "基类A" << endl; }
private:
	int a;
};
class B				//基类
{
public:
	B(int y):b(y){ cout << "基类B" << endl; }
private:
	int b;
};
class C: public B, public A//派生类,此处红色部分为派生类对基类的声明
{
public:
	C(int x,int y,int z):A(x),B(y),c(z){ cout << "派生类C" << endl; }
private:
	int c;
};
int main()
{
	C obj(1,2,3);
	return 0;
}
  • 如果派生类的基类也是一个派生类,则每个派生类只需负责其直接基类的构造,依次上溯。
class A			//基类
{
public:
	A(int x):a(x){}
private:
	int a;
};
class B:public A	//派生类
{
public:
	B(int x, int y):A(x), b(y){}
private:
	int b;
};
class C: public B	//派生类的派生类
{
public:
	C(int x,int y,int z):B(x,y),c(z){}
private:
	int c;
};
int main()
{
	C obj(1,2,3);

	return 0;
}
  • 派生类构造函数负责调用基类构造函数,并为其提供所需的参数,以保证基类初始化时能够获得必要的数据。如果基类的构造函数定义了一个或多个无默认值的参数时,派生类必须定义构造函数。
  • 如果基类定义了无参数的构造函数或由编译器自动生成缺省构造函数或构造函数的所有参数都有默认值时,在派生类构造函数的定义中可以省略对基类构造函数的调用,即初始化列表省略“基类名(参数表)”。如果此派生类也没有子对象需要初始化,那么就可以完全省略派生类构造函数的初始化列表。

析构函数

  • 当派生类对象消亡时,系统会自动调用派生类的析构函数,做一些必要的清理工作
  • 在继承过程中,派生类不能继承基类的析构函数,所以派生类需要自己定义析构函数
  • 在执行派生类的析构函数时,基类的析构函数将被自动调用
  • 析构派生类对象的步骤顺序是:先调用派生类的析构函数析构派生类对象新增部分,如果该派生类含有子对象,接下来就析构子对象部分,最后调用基类的析构函数析构基类部分,派生类的析构顺序恰好与构造顺序相反

多继承

  • 在多继承中,当派生类的构造函数需要初始化多个基类的数据成员时,一般习惯是在 定义派生类的构造函数的初始化列表中按照派生类中声明的各个基类的先后顺序来逐一提供参数并调用相应基类的构造函数(即基类名(参数表) )来完成
  • 二义性问题
    • 多重继承带来的最常见的问题是因为从不同基类继承来的成员同名而产生的二义性
    • 解决二义性问题办法
      在调用基类同名的成员函数时,加上作用域限定符

虚基类

由前面的问题看出,我们可以加上作用域限定符的方式来解决二义性问题,其实还可以有一种更好的办法就是虚基类

  • 虚基类由关键字virtual标识,一般语法格式如下:
class  派生类名: virtual 继承方式 基类名
  • 注意事项
    • 虚基类的构造函数在非虚基类之前调用
    • 若同一层次中包含多个虚基类,则按它们在派生类中声明虚基类时被指定的顺序调用
    • 若虚基类由非虚基类派生而来,要先调用更高级别基类的构造函数,再遵循上述1和2的顺序
class A;
class B;
class C : public A, virtual B
{
};
/*将产生如下的调用次序:
B( )
A( )
C( )*/

组合类

  • 类的组合指在一个类的数据成员中含有一个或多个类对象,这种以数据成员身份出现的类对象就叫子对象。组合类创建的对象和子对象之间是一种“整体与部件”的关系,也是“拥有(has a)”关系
  • 当具有“一种(is a kind of)”关系时,适合用继承,例如轿车是一种交通工具,这时可将轿车定义为交通工具的派生类
  • 当具有“拥有(has a)”关系时,适合用组合类,例如轿车又拥有发动机等零部件,这时最好将发动机等部件定义为轿车的子对象

考点总结

  • 关于继承中三个权限的区分,要理解清楚
  • 在继承过程中,构造函数和析构函数的先后顺序要把握
  • 什么情况下必须使用列表初始化(如初始化基类或子对象时)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值