继承及虚继承

一. 继承
首先,回忆继承的基本知识
1. 派生:从已有类产生新类的过程就是类的派生;
    继承:派生类继承了已有类的所有数据成员,得到了已有类的所有特性。对于继承层次来说,越往上越公有,越往下越具体。
2. 派生类会继承基类的所有数据成员。派生类不能对接收的基类数据成员进行选择,但是可以对其进行访问方式的调整。
    第一种调整方式:子类继承父类的方式的不同,导致父类成员在子类中的访问限制发生改变;
    第二种调整方式:同名覆盖。在子类中新增与父类同名的数据成员或成员函数(返回值类型,函数名,参数列表都要相同),
                                 此时,这个新增的成员的作用域在派生类中。若要调用父类中的同名成员,需要加上作用域运算符。
3. 继承的三种方式以及父类成员在子类中的访问情况
(1)公有继承
(2)保护继承
(3)私有继承
总结:
    公有继承方式下,基类中的数据成员保持其原有属性不变。
    保护继承方式下,基类中的公有成员变为保护成员。保护成员的特性是,在类内可以直接使用,但是在类外不能被直接使用。
    基类中的私有成员在任何继承方式下在子类中仍为私有存在,且基类的私有成员在子类中永远是存在且不可访问的。
4. 继承的分类
                      
              单继承                                                     多继承                                                             菱形继承
问题一:分析protected成员和private成员在不同继承方式下的区别
1. protectd成员
    在public和protected继承方式下,保护成员在派生类中仍为保护成员;在private继承方式下,保护成员在派生类中变为私有成员。
    若在派生类中以私有方式存在,则该数据不能被直接访问。
    当protected成员在派生类中仍以保护形式存在时,它在派生类中可以直接被访问;但是在派生类外,保护成员不能通过派生类的
    对象成员而被访问到。
2. private成员
    无论在何种继承方式下,私有成员在派生类中永远都是存在且不可访问。

二. 赋值兼容规则
简单回忆C++中赋值兼容规则的基本知识
1. 赋值兼容:不停类型数据之间的相互转化和赋值。
2. 在派生类对象和基类对象之间也存在着赋值兼容关系。当需要用到基类对象成员时。用公有派生类对象给基类对象进行赋值。
3. 公有继承下的派生类对象具有基类的所有数据,可以将派生类对象的值赋给基类,以此替代基类的数据成员。
(1)派生类对象可以对基类对象(或基类对象的引用)进行赋值
         Base b;
         Derived d;
         b=d;   //正确。
         Base &c=d;  //正确。
(2)派生类对象的地址可以赋给基类对象的指针
         Derived d;
         Base *b=&d; 
(3)基类的对象指针只能指向公有派生类成员的地址,不能指向私有派生类成员的地址。
(4)基类的对象指针可以指向派生类对象的地址;派生类的对象指针不能指向基类对象的地址。
下面我们来分析这些规定得到本质原因:
首先,需要了解”切片/切割“的概念
所以当子类对象赋值给父类对象时,编译器会自动发生切片行为。将子类中从父类继承来的成员划分出来,赋值给父类;
而父类对象不能赋值给子类对象是由于子类对象继承了父类对象的所有数据后又新增了只属于自己的数据,所以用父类对象的内容不能涵盖
子类对象的所有数据要求,故不能用父类对象给子类对象进行赋值操作。
又有父类的对象指针(或对象引用)可以指向子类的对象地址;而子类的对象指针不可以指向父类的对象地址,这又是为什么呢?
分析上段代码
person 类为基类,其中存有数据成员 name、age
student类为派生类,继承了激烈的数据成员name、age且自身新增成员num
当用基类对象指针指向派生类对象时,能访问到的只是派生类从基类中继承而来的成员;而派生类自身的新增成员则不能被基类指针访问到。
基类指针可以指向派生类对象而派生类对象指针不能够指向基类对象。这是因为派生类对象所占的存储空间比基类大,若用派生类对象指针
访问基类的对象成员就会发生越界,访问到不属于基类对象的内容,造成程
总结:
子类成员可以赋值给父类,发生切片行为;父类成员不能赋值给子类,因为数据类型不能满足子类需要。
父类对象指针可以指向子类对象的地址;子类对象指针不能指向父类对象的地址。

菱形继承引出虚基类
#include<iostream>
using namespace std;
class Base{       //基类Base
protected:
	int a;
public:
	Base()
	{ a=5;
	cout<<"Base a="<<a<<endl;}
};
class Base1: public Base{//声明类Base是类Base1的基类
public:
	int b1;
	Base1()
	{ a=a+10;
	cout<<"Base1 a="<<a<<endl;}
};
class Base2: public Base{//声明类Base是类Base2的基类
public:
	int b2;
	Base2()
	{ a=a+20;
	cout<<"Base2 a="<<a<<endl;}
};
class Derived:public Base1,public Base2{//多基派生Derived公有多重继承了Base1,Base2
public:
	int d;
	Derived()
	{ cout<<"Derived a="<<a<<endl;}
};
int main()
{
	Derived obj;
	return 0;
}
在此例中,基类Base派生出Base1和Base2,派生类Derived又继承了Base1和Base2,四个类构成一个菱形继承。
(1)在菱形继承中会出现二义性和数据的冗余现象。Base1和Base2同时继承了基类Base,Base1和Base2中都存有Base的成员;Derived类
         再继承Base1和Base2后会继承这两个类内的所有成员。所以,在Derived类中存有两份Base类数据的拷贝,造成了数据的冗余。
(2)值的注意的是Base1和Base2中重基类Base继承而来的数据虽然数据名相同,但是所占的存储空间不同,本质上来说是两个不同的数据。
         所以在Derived类中调用基类Base中的数据时会产生二义性,不能分清楚调用的是Base1中的数据还是Base2中的数据。
为了解决这样的问题,我们引入虚基类(声明共有基类为直接基类的虚基类)
虚基类的表达形式:class 派生类名 :继承方式 基类名
将公有基类声明为直接基类的虚基类后,在派生类中只进行一次共有基类成员的复制,避免了数据冗余和二义性的出现。




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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值