C++_继承

1、什么是继承?

    继承机制是面向对象程序设计使代码可以复用的最重要的手段,它允许程序员在保持原有类特性的基础上进行扩展,增加功能,这样产生新的类,称派生类。继承呈现了面向对象程序设计的层次结构,体现了由简单到复杂的认知过程。继承是类设计层次的复用。

1.1、struct在C语言与C++中的区别?

    C语言中:struct是用户自定义数据类型,是一种数据结构的是实现体。
    C++中:struct是抽象数据类型,支持成员函数的定义,是一种对象的实现体。

1.2、C++中class和struct的区别?
  1. C++中的struct其实等同于类,只是class的默认访问权限是private,而struct的默认访问权限是public。(C++要兼容C语言)
  2. class默认继承权限是private,struct默认继承权限是public。
  3. 在使用模板的时候 template <class T>/template<typename T>,不能使用template<struct T>

2、赋值兼容规则

    public继承方式:is-a,可以将一个子类对象看成是一个基类对象,在类外所有使用基类对象的位置都可以使用子类对象代替。
    对象模型:对象中各个成员变量在内存中的布局方式。
在这里插入图片描述

class Person
{
protected :
	string _name; // 姓名
	string _sex;  // 性别
	int _age; // 年龄
};
class Student : public Person
{
public :
	int _No ; // 学号
};
void Test ()
{
	Student sobj ;
	// 1.子类对象可以赋值给父类对象/指针/引用
	Person pobj = sobj ;
	Person* pp = &sobj;
	Person& rp = sobj;
	//2.基类对象不能赋值给派生类对象
	sobj = pobj;
	// 3.基类的指针可以通过强制类型转换赋值给派生类的指针
	pp = &sobj
	Student* ps1 = (Student*)pp; // 这种情况转换时可以的。
	ps1->_No = 10;
	pp = &pobj;
	Student* ps2 = (Student*)pp; // 这种情况转换时虽然可以,但是会存在越界访问的问题
	ps2->_No = 10; 
}
  1. 可以使用子类对象给基类对象来进行赋值,反之不行;
  2. 可以使用基类的指针或引用指向子类的对象;
  3. 不能使用子类的指针或引用指向基类的对象。

3、同名隐藏

    在继承体系中,子类和基类具有相同名称的成员(成员变量||成员函数),如果使用子类对象访问相同名称的成员,优先访问的是子类自己的,基类相同名称的成员无法访问到。
注意:
    >>成员函数:与函数原型是否相同没有关系;
    >>成员变量:与成员变量的类型是否相同没有关系。

在继承体系中子类对象构造过程

  1. 基类如果没有定义任何构造函数,则子类可以定义也可以不定义;
  2. 如果基类显式定义了无参的构造函数||全缺省的构造函数,则子类可以根据自己需求选择给出对应构造函数。即:可以定义也可以不定义,如果子类没有显式定义时,则编译器可以替用户实现一个默认的构造函数,目的:将子类对象中从基类继承下来的成员构造完成。
  3. 如果基类显式定义了构造函数,并且构造函数不是无参||全缺省的构造函数,则子类必须要显式定义自己的构造函数,然后需要在其构造函数初始化列表的位置显式调用基类构造函数完成基类部分成员的初始化,否则代码就会编译失败
  4. 子类对象构造和析构的过程:
    构造:先调用基类构造函数,然后调用子类构造;
    析构:先调用子类的析构函数,然后再调用基类的析构函数。
    注意:打印结构并不能代表函数的执行次序。
    构造函数:创建哪个类的对象,调用哪个类的构造函数,先要执行初始化列表完成对象中成员的初始化,然后再去执行构造函数中的其它代码。
    析构函数:销毁哪个类的对象,调用哪个类的析构函数。编译器会在子类析构函数最后一条有效语句之后,插入一条汇编语句,call 基类的析构函数。

4、菱形继承

单继承:一个子类只有一个直接父类
在这里插入图片描述

多继承:一个子类有两个或者以上直接父类
在这里插入图片描述

菱形继承:菱形继承是多继承的一种特殊情况
在这里插入图片描述

菱形继承的问题:从下面的对象模型构造,可以看出菱形继承有数据冗余和二义性的问题,在Assistant的对象中Person成员会有两份。
在这里插入图片描述

4.1、菱形继承的二义性问题

在这里插入图片描述

  1. 让访问明确化;d.C1::_b = 1;
  2. 让最顶层基类中成员在子类中只存储一份(菱形虚拟继承)
4.2、虚拟继承:

    注意:是在C1和C2继承中添加virtual关键字。
在这里插入图片描述
在这里插入图片描述
虚基表中存放了:1、该子类对象相对于自己的偏移量;2、该子类对象相对于基类部分成员起始位置的偏移量。

  1. 对象中多了4个字节;
  2. 如果用户没有显式定义构造函数,则编译器会给子类生成一份默认的构造函数;
    或者如果子类显式定义了构造函数,则编译器会对子类构造函数进行修改(向对象前4个字节中填充数据)
  3. 对象模型布局方式不一样。

    下图是菱形虚拟继承的内存对象成员模型:这里可以分析出D对象中将A对象放到了对象组成的最下面,下面这个A同时属于B和C,那么B和C是如何找到公共的A的呢?这里是通过了B和C的两个指针,指向的一张表。这两个指针叫虚基表指针,这两个表叫做虚基表,虚基表中存的偏移量,通过这个偏移量可以找到下面的A。
在这里插入图片描述
下面是上面的Person关系菱形虚拟继承的原理解释:
在这里插入图片描述

5、继承和组合

  • public继承是一种 is-a 的关系,也就是说每个派生类对象都是一个基类对象;
  • 组合是一种 has-a 的关系,假设B组合了A,每个B对象中都有一个A对象。
  • 优先使用对象组合,而不是类继承。
  • 继承允许你根据基类的实现来定义派生类的实现。这种通过生成派生类的复用通常被称为白箱复用。术语“白箱”是相对可视性而言:在继承方式中,基类的内部细节对子类可见。继承一定程度破坏了基类的封装,基类的改变,对派生类有很大的影响。派生类和基类间的依赖关系很强,耦合度很高。
  • 对象组合是类继承之外的另一种复用选择。新的更复杂的功能可以通过组装或组合对象来获得。对象组合要求被组合的对象具有良好定义的接口。这种复用风格被称为黑箱复用,因为对象的内部细节是不可见的。对象只以“黑箱”的形式出现。组合类之间没有很强的依赖关系,耦合度低。优先使用对象组合有助于你保持每个类被封装。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值