继承和多态,菱形继承,纯虚继承

类与类之间的关系:嵌套,代理,继承

组合,嵌套:一个类中声明了另一个类,一个类是另一个类的一部分

代理:一个类的接口是另一个类的接口的子集,一个类的功能需要依赖另一个类的功能实现

继承:一个类是另一个类的一种

构造析构顺序:先构造父类再构造子类,析构先子类后父类

赋值兼容规则:当派生类公有继承时,可以用派生类的对象对基类的对象赋值,这时会将派生类继承来的变量的值赋给基类。

父类的构造函数如果需要传参,就必须在子类的构造函数中初始化列表初始化

同名隐藏:子类会隐藏父类的同名成员(与参数类型无关),对派生类的类外也隐藏,因此在类内类外访问都必须加::作用域

父类的指针指向子类的对象:Base* pb=new Derive();

#include<string.h>
class Fish
{
 public:
     string _name; 
};

class GoldFish:public Fish
{
 public:
    string _color;
};

int main()
{
   Fish fish;
   fish._name="jinyu";
   GoldFish gfish;gfish继承了父类,拥有父类的属性_name;
   gfish._name="jinyu";
   gfish._color="gold";
   return 0;
}

public:任何地方都可以访问

protected:子类和自身类可以访问

private:只有自身类可以访问


class Person
{
  public:

   Person(string name,int age,string sex,string wife=0)
  {
     _name=name;
     _sex=sex;
     _age=age;
     _wife=wife;
  }
  void eat()
  {
  cout<<"eat"<<endl;
  }
  void work()
  {
     cout<<"work"<<endl;
  }
  protected:
       string _name;
       int age;
       string _sex;
  private:
    string _wife;
    
};


class  student:public Person
{
  public:
    student(string name,int age,string sex,string num)
       :Person(name,age,sex,wife)必须初始化,因为父类先构造
    {
       _num=num;
    }
     void show()
     {
       cout<<"name"<<_name<<endl;
       当_name为父类的私有成员时报错,子类无法访问父类的私有成员,共有与保护不报错
       cout<<"age"<<_age<<endl;
     }

  protected:
  private:
  string _num;
};

int main()
{
   student s;
   
} 

多继承


class Base基类
{
  public:
     void fun()
     {
        cout<<"base void fun1()"<<endl;
     }
     void fun(int a)
     {
        cout<<"base fun(int a)"<<endl;
     }
  private:
     int a;
  
};

class Derive:public Base派生类
{
  public:
     void fun()
     {
       cout<<"derive fun"<<endl;
     }
     void fun(int a,int b)
     {
        cout<<"derive void fun(int a)"<<endl;
     }
  private:
       int _b;
     
};

int main()
{
   Derive d;
   d.fun();调用derive fun
    d.base::fun();调用Base的fun
    d.fun(2);报错
    子类会隐藏父类的同名成员,只要名字一样就会隐藏,所以会报错,可以用::来调用
    Base* bp=new Derive();父类的指针指向子类的对象,
    bp->fun(10);没用作用域不报错,它认为bp指向的是父类的对象
    delete bp;调用base的析构,然后子类的析构就无法实现,方法是在Base的析构函数前加(virtual}

在这里插入图片描述
在这里插入图片描述

动多态

动多态产生:使用指针或者引用调用虚函数,就会产生动多态调用

动多态调用的过程
1.使用指针或者引用调用虚函数
2.在对象中找到vfptr
3.根据vfptr找到vftable
4.在vfptr中找到要调用的函数
5.调用

虚函数具有传递性,当基类的函数加上virtual,子类的同函数不加virtual也会被编译器处理为虚函数,包括析构函数
同函数:同返回值,同函数名,同参数列表

编译时期如果类中具有虚函数就会对这个类生成vftable(虚函数表),将当前类虚函数的指针放到vftable当中

当对象进行构造时,如果发现该类有vftable,就会将vftable的地址写入到这个类对象之中(vfptr)
每一个类中有一个虚函数表(放在.rodata)只读数据段

覆盖(重写):发生在vftable,父类中的虚函数会被子类中的相同的函数覆盖(在子类的虚函数表中覆盖)

当基类的一个函数为虚函数,那么用基类的指针指向多个派生类,指向那个调用的就是那个派生类

继承权限
在这里插入图片描述

菱形继承

在这里插入图片描述
菱形继承:D中既有B继承的A的函数又有C继承A的函数,调用该函数时会报错,因为不知道调用的是哪个

解决方法是将B C继承时加一个关键字virtual(虚继承),加关键字后,B,C中就会存在一个虚基类指针(vbptr)指向vbtable中虚基类实例的位置(A).对于构造函数来说:A的构造函数必须在D类重新初始化列表构造,不能说D继承了BC(BC中已经构造了A)就不用构造,当BCvirtual继承时,那么A也相当于D的直接父类。
菱形继承的的内存分配:先总的开辟一个大空间,然后构造B中的A类,它们占一块空间,再构造B类,再构造c中的A类,最后构造本类。
为了解决菱形继承带来的数据冗余,在BC类继承A时加一个virtual,并且在D类的构造函数中(初始化列表参数)对A类进行构造,A(id,name)这样构造时内存分配是先开辟大空间,然后在底部构造A类,再构造B和C,B和C中有一块空间存放A的偏移地址。

菱形继承下若不是虚继承的虚函数产生的虚表,对于D来说有两份,先继承的B的虚表是真的虚表,对于继承的C的表在调用时会产生转换器直接调用继承B的。

纯虚函数

有纯虚函数的类称为抽象类,抽象类不能实例化对象
限制子类必须覆盖某一个接口

class A
{
   public:
      virtual void fun()=0;纯虚函数
}
class AA
{
   public:
       virtual void fun()
       {
          cout<<fun<<endl;
       }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值