继承的相关知识

1. 继承的概念

  1. 定义:继承是面向对象复用的重要手段,即面向对象的可重用性是通过“继承”来实现的。解决在已有的类中增加新的特性,减少重复的工作量的问题。
  2. 已经存在的类,叫“基类”或“父类”;建立的新的类,叫“派生类”或“子类”。继承是类型之间的关系模型,共享公有的东西,实现类内各自不同的东西。
  3. 继承的定义格式为:
    class DeriveLcassName(派生类):acess-label(继承类型)BaseClassName,其中冒号后面的统称为派生类列表。
  4. 继承的分类:
    继承分为单继承和多继承。
    (1) 单继承是一个派生类只从一个基类派生,称为单继承。
    示意如下:
    这里写图片描述
    图中,箭头表示继承的方向,从派生类继承于基类。
    下面是简单的单继承代码:
class Person
{
public:
    void Display()
    {
        cout<<_name<<endl;
    }
protected:
  string _name;   //姓名
private:
    int _age;

};
class Student: public Person
{
protected:
    int _num;    //学号

};
void Test()
{
    Person p;
    Student s;
    //1.子类给父类
    p = s;
    //2.父类给子类
    // s = p;
    //3.父类通过指针/引用给子类
    Person *p1 = &s;
    //Person &p1 = s;
    //4. 子类的指针/引用不能指向父类对象(可以通过强制转换完成)
    Student *p2 = (Student *)&p;
    //Student &s = (Student &)p;
    //p2->_num = 10;//不可访问
    //p2._num = 10;
}

(2)多继承:是一个派生类有两个或多个基类,称为多重继承。
简单示意如下:

这里写图片描述

简单的对多继承的理解,代码如下:

class Person
{
public:
    friend void Display(Person &p,Student &s);
    void buy()   //虚函数
    {
        cout<<"买东西"<<endl;
    }

   protected:
    string _name;
};
class Student:public Person
{
public:
    //virtual//
    void display_1()
    {
        cout<<"买半价"<<endl;
        cout<<Person::_name<<endl;
    }
protected :
    int _strNum;

  protected:
    //int _num;

};
void Display(Person &p,Student &s)
{
    cout<<p._name<<endl;
    cout<<s._strNum<<endl;
}

void Fun()
{
    Person p;
    Student s;
    Display (p, s);
}
int main()
{
    Person p;
    Student s;
    //cout<<p._name<<endl;
    //Fun(s);
    return 0;

}

2. 继承中的规则

  1. 继承关系中构造、析构函数调用顺序
    (1)构造函数调用顺序
    这里写图片描述
    (2)析构函数调用顺序
    这里写图片描述
  2. 继承与转换–赋值兼容规则
    (1)子类对象可以全部赋值给父类;
    (2)父类对象不能赋值给子类对象;
    (3)父类的指针或引用可赋值给子类对象;
    (4)子类对象的指针或引用不能赋值给父类对象,但可通过强制类型转换完成。

3. 菱形继承

菱形继承是由单继承和多继承组合的一种复杂的继承关系,存在二义性和数据冗余的问题。而菱形虚拟继承则是为了解决菱形继承所存在的问题。
菱形继承的简单示意图如下:

这里写图片描述
其中派生类D既继承于C1,又继承于C2,通过派生类调用基类中的成员或成员函数时会产生二义性的问题
模型如下图:
这里写图片描述
简单模拟代码如下:

//菱形继承
#include<iostream>
using namespace std;

class B
{
public:
    int _b;
    int _b1;
};
class c1:public B
{
public:
    int _c1;
    int _c;
};
class c2:public B
{
public:
    int _c2;
};
class D:public c1,public c2
{
public:
    int _d;
    int _d1;
};
int main()
{
    D d1;

    d1._d = 1;
    d1._d1 = 4;
//  d1._b1;            //这里不能通过编译,现象对D::_b1访问不明确,出现模糊调用的情况
    d1.B::_b1 = 3;    //这里能通过编译,但仍会显示对基类“B”访问不明确
    return 0;
}

在对基类中的成员变量访问时,必须使用域访问限定符,否则编译器无法识别是哪个对象中的_b1赋值,虽然解决了二义性问题,但是产生了数据冗余.
为了解决菱形继承产生的二义性和数据冗余的问题,采用了菱形虚拟继承的方法,现对菱形虚拟继承概括:

4 菱形虚拟继承

  1. 定义:菱形虚拟继承是在继承类型前加入虚拟(virtual)关键字,且使用的是偏移量表格。
  2. 同名隐藏规则:
    虚基类是只有一个拷贝;
    作用分辨符(::)的访问方式:基类::成员(参数)/成员
    同名隐藏规则:没有虚函数的情况下,派生类中新增的与基类中的函数同名的函数,即使参数个数不同,则基类中的同名函数被覆盖;如果派生类中的多个基类拥有同名成员函数,派生类也新增了同名函数,则基类都被隐藏(这种情况,对派生类的访问用对象名。成员名,对基类的用作用分辨符)。
    1. 菱形虚拟继承关系:
      继承关系如下图所示:
      这里写图片描述

菱形虚拟继承模型如下:
这里写图片描述
如图所示:
Address1,Address2这里均存的是指针,指针指向偏移量表格。偏移量表格中先存该派生类相对于自己的地址,后存该派生类相对于基类的地址。
代码及分析如下:

 //菱形虚拟继承
#include<iostream>
using namespace std;

class B
{
public:
    int _b;
    int _b1;
};
class C1:virtual public B
{
public:
    int _cc;
    int _c;
};
class C2:virtual public B
{
public:
    int _c2;
};
class D:public C1,public C2
{
public:
    int _d;
    int _d1;
};
void test()
{
    D d1;

    d1.C1::_b = 2;
    d1.C2::_b = 7;

    d1._b1 = 3;
    d1._d = 1;
    d1._d1 = 4;
}
int main()
{
    test();
    return 0;
}   

在调试过程中,先看看d1中存了些什么:
这里写图片描述
能看出d1存储了指针变量的地址,指针变量的内容是指向虚基类的地址,是4个字节,然后再看看这个地址中存放了什么。

这里写图片描述
1c转换成十进制是28,32位平台下一个地址是4个字节,因此,指向的是继承关系的模型中的_d1
好了,基本的先总结到这里~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值