继承与派生类

目标:

  1. 继承概念、怎么定义继承和派生类
  2. 继承关系和访问符
  3. 创建派生类对象时怎么调用构造析构
  4. 继承赋值规则
  5. 了解单继承、多继承、菱形继承

    • 继承
      通过继承联系在一起的类构成一种层次关系。根部是基类,其他类则是直接或间接从基类继承而来,这些类成为派生类。基类的定义的层次关系中所以类都可以拥有的成员,派生类则是定义自己特有成员。
      这里写图片描述
      继承呈现了 面向对象程序设
      计的层次结构, 体现了 由简单到复杂的认知过程。

    • 继承的定义

继承首先要有基类这里定义一个基类

class Base
{
public:
    Base()
    {
        cout << "Base()" << endl;
    }
    void FunTest()
    {
        cout << "BFunTest()" << endl;
    }
public:
    int _b2;
protected:
    int _b1;
private:
    int _b;
};

class+派生类(子类)+:+继承类型+基类(父类)
例:class Derived:public Base{}
继承类型:基类成员在派生类的可见性(public、protected、private)

  • 继承关系与访问限定符
    1. public成员:
      可以在派生类中和类外被访问。
    2. protected:
      只能在基类和派生类中被访问。
    3. private:
      基类的private成员在派生类中是不能被访问的, 如果基类成员 不想在类外直接被访问, 但需要
      在派生类中能访问, 就定义为protected。 可以看出保护成员限定符是因继承才出现的。
    4. 使用关键字class时默认的继承方式是private, 使用struct时默认的继承方式是public, 不过最
      好显示的写出继承方式。
      这里写图片描述
      另外如下
class Base
{
public:
    Base()
    {
        cout << "Base()" << endl;
    }
    void FunTest()
    {
        cout << "BFunTest()" << endl;
    }
private:
    int _b;
};
class Derived :public Base
{
public:
    C()
    {
        cout << "C()" << endl;
    }
    void FunTest()
    {
        cout << "CFunTest()" << endl;
    }
private:
    int _c;
};
int main()
{
  C c;
  c.FunTest();
  return 0;
}

试问这样访问的是基类的FunTest()函数还是派生累里的FunTest()函数呢?
答案是派生类里的
子类和父类中有同名 成员 , 子类成员 将屏蔽父类对成员 的直接访问。 ( 在子类成员函数中,可以
使用 基类: :基类成员 访问) –隐藏 –重定义
所以实际中在继承体系里面最好不要定义同名的成员 。

  • 构造函数调用顺序

这里如果我们创建一个Derived对象 Derived c,那编译器是先调用基类的构造函数,还是派生类的构造函数呢?

class Base
{
public:
    Base()
    {
        cout << "Base()" << endl;
    }
    ~Base()
    {
        cout << "~Base" << endl;
    }
    void FunTest()
    {
        cout << "BFunTest()" << endl;
    }
public:
    int _b2;
protected:
    int _b1;
private:
    int _b;
};
class C :protected Base
{
public:
    C()
    {
        cout << "C()" << endl;
    }
    ~C()
    {
        cout << "~C()" << endl;
    }
    void FunTest()
    {
        _b1 = 0;
        _b2 = 0;
        cout << "CFunTest()" << endl;
    }
public:
    int _c2;
protected:
    int _c1;
private:
    int _c;
};
void FunTest()
{
    C c;
}
int main()
{
    FunTest();
    system("pause");
    return 0;
}

结果图:
这里写图片描述
这样看 是先调用基类的构造函数然后再调用派生类的,可是真的是这样吗?
我们来看看反汇编
这里写图片描述
F11进去发现
这里写图片描述
再F11
这里写图片描述
从这可以看到当我们创建一个派生类对象的时,首先调用的是派生类自己的构造函数,然后再调用基类的构造函数,刚才那个是因为代码执行的时候派生类函数里的输出函数在Base输出函数之后执行的。
所以我们一个在派生类构造函数的初始化列表位置显示调用基类

析构函数没有问题,先调用谁的构造函数就先析构谁。
总结:
这里写图片描述

这里写图片描述
另外要记得
1、 基类没有缺省构造函数, 派生类必须要在初始化列表中显式给出基类名 和参数列表。
2、 基类没有定义构造函数, 则派生类也可以不用定义, 全部使用缺省构造函数。
3、 基类定义了 带有形参表构造函数, 派生类就一定定义构造函数

  • 继承赋值规则
  • 就是父=子,不能 子=父.
  • 父类指针引用可以子类而指针引用不能指向父类.(不过可以强制类型转换)
    有一个典型的话:
    你长的真像你爸,而不能说你爸长的真像你。

  • 友元关系与继承



  • 静态成员与继承
    如果在基类中定义一个静态,则整个继承体中有且仅有这个成员。

  • 单继承、多继承、菱形继承

    1. 单继承:
      就相当于这个家庭都是独生子女,一个子类只有父类
      这里写图片描述
    2. 多继承:
      一个子类有多个父类。
      这里写图片描述
    3. 菱形继承:
      这里写图片描述
      这里菱形继承会发现一个问题

例如:

class A
{
public:
    int _a;
};
class B :public A
{
protected:
    int _b;
};
class C :public A
{
protected:
    int _c;
};
class D :public B,public C
{
protected:
    int _d;
};
void FunTest()
{
    D d;
    d._a;
}
int main()
{
    FunTest();
    system("pause");
    return 0;
}

编译器会报一个错误
这里写图片描述
D的中有两份A的成员这是菱形继承存在的和冗余的问题。
这个问题用虚继承可以解决,所以之后再分析虚继承是如何做到的

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值