C++的特质——继承

继承

  • 被继承的作为父类(基类)
  • 继承父类的作为子类(派生类)
  • 子类继承父类的所有特性

对于继承的描述

  • 继承作为一个典型的树状结构
  • 从基类向下延伸,形成各种分支
  • 分支具有向上分支的特性,同时因自己的独特特性与其他分支区分开
  • 分支越向下必然是描述的越来越精细
  • 继承机制是面向对象程序设计使代码可以复用的最重要的手段,它允许程序员在保持原有的特性基础上进行扩展,增加功能,增加属性,这样产生新的类,称作是派生类。
  • 继承呈现了面向对象程序设计的层析结构,体现了由简单到复杂的认知过程。继承是类设计层次的复用。
  • 举例: 以people类作为基类,向下延伸的student类,teacher类,worker类,这样的类之后再延伸出带有新的特性的子类
    以people->worker->salesman为例,salesman具有其worker的特性,区别于worker,salesman多出了自己的新特性,新的功能。

继承程序举例

#include <iostream>
#include <string>
using namespace std;

class people
{

public:
    people(string name, string sex, int age) : m_name(name), m_sex(sex), m_age(age){};

public:
    string m_name;
    string m_sex;
    int m_age;
};

class student : public people
{
public:
    student(string name, string sex, int age, int sid) : people(name, sex, age), m_sid(sid){};

    void print_s()
    {
        cout << m_name << ","
             << m_sex << ","
             << m_age << ","
             << m_sid << endl;
    }

public:
    int m_sid;
};

class representative : public student
{
public:
    representative(string name, string sex, int age, int sid, string course) : student(name, sex, age, sid), m_course(course){};

    void print_r()
    {
        cout << m_name << ","
             << m_sex << ","
             << m_age << ","
             << m_sid << ","
             << m_course << endl;
    }

public:
    string m_course;
};

int main()
{
    representative r("王明", "男", 20, 1, "数学");
    r.print_s();
    cout << endl;
    r.print_r();
    return 0;
}

运行结果:

王明,,20,1

王明,,20,1,数学
  • 上例实现的是公有成员与公有继承。保护成员与继承、私有成员与继承的实现读者可以自行尝试。
    以上程序,旨在于体现继承的作用,子类继承父类的属性与方法,并可以实现调用父类的方法

Tips

  • 子类拥有父类的所有特性
  • 继承方式有公有继承,私有继承,保护继承。默认是私有继承。
  • 公有继承中父类的公有和保护成员在子类中不变,私有的在子类中不可访问。(public)
  • 私有继承中父类的公有和保护成员在子类中变为私有,但私有的在子类中不可访问。(private)
  • 保护继承中父类的公有和保护成员在子类中变为保护,但私有的在子类中不可访问。(protected)

继承的优缺点

  1. 首先,最明显的好处,顾名思义的继承,父辈走过的路,后辈如果想去走相同的路的话,不用再走一遍,父辈的工具(定义好的函数方法)与经验(定义好的使用方法需要的属性),只要后辈继承了,那就都属于后辈,减少重复的代码,父辈私有(private)的除外
  2. 增加了类之间的关联程度,即耦合度(既是优点,也是缺点)
  3. 继承的使用就意味着父类至少定义了子类的部分行为,父类的改变会影响所有继承的该父类的子类
  4. 子类会直接拷贝父类的内存空间,如果父类继承的东西,子类没有都用到,就是浪费空间,少用一个就多浪费一点。当然,这个浪费只是这样说,只要不是父类弄的特别大特别全,然后继承过来就用一点,都不算太过分。总的来说,继承减少了代码量,但是该给的内存空间一点没少,多数情况还要多出一点作为没有使用到拷贝空间的所有内容的浪费。

关于继承体系中的成员函数的几种情况

重载:写一个与已有函数同名但是参数列表不同的函数

重写:对函数的重写,函数列表与参数列表都相同,但是对内部实现进行修改

隐藏:子类对象、指针、引用访问基类和派生类都有同名函数时,先访问子类的函数,即隐藏父类的同名函数。更通俗的讲,父类定义过的函数,子类又重新定义了一次,这时子类对象用函数,优先用子类里面的,子类里面找不到再往上找。

继承体系里几种情况,相比较的关系

继承体系同名成员函数的关系
作用域函数名称参数返回值
重载同一作用域相同名称不同可以不同
隐藏(覆盖)不同作用域,父类与子类相同名称相同相同
重写不同作用域,父类与子类,父类中必须要有virtual相同名称相同相同(协变除外)

注,协变的定义:父类(或子类)的虚函数返回父类(或子类)的指针或引用

对继承的补充:

一,多继承

子类只继承一个父类的情况被称为单继承
此外,C++也支持多继承,即一个子类继承了多个父类——多继承
形如:

class C : public A, public B//这里的继承的方式不止于public,protected、private都可以正常使用,与正常的继承方式相同,多继承只是多了父类,没有什么特别的。
{
}

多继承下的构造函数如果想要调用不同父类中的参数,当然也不同于单继承
形如:

class C : public A, public B
{
public:
    C(int a, int b, int c) : A(a), B(b)//多继承下的构造函数
    {
    }
    ~C()
    {
    }
    int m_c;
};

多继承完整程序举例:

#include "iostream"
using namespace std;

class A
{
public:
    A(int a)
    {
    }
    ~A()
    {
    }
    int m_a;
};

class B
{
public:
    B(int b)
    {
    }
    ~B()
    {
    }
    int m_b;
};

class C : public A, public B
{
public:
    C(int a, int b, int c) : A(a), B(b)
    {
    }
    ~C()
    {
    }

    int m_c;
};

int main()
{
    C c(1, 2, 3);

    cout << &c << endl;
    cout << &c.m_a << endl;
    cout << &c.m_b << endl;
    cout << &c.m_c << endl;

    cout << sizeof(c) << endl;

    return 0;
}

二,虚继承

虚继承作为解决多继承产生的问题的一种手段。

多继承主要产生两种问题:

  1. 多个子类继承了同一父类,每个子类都拷贝了一份父类的内存空间,显而易见,浪费了存储空间
  2. 数据的二义性问题。具体的举例来说,“菱形”问题:有最基类A,有A的派生类B、C,又有D同时继承B、C,那么若A中有成员a,那么在派生类B,C中就存在a,又D继承了B,C,那么D中便同时存在B继承A的a和C继承A的a,那么当D的实例调用a的时候就不知道该调用B的a还是C的a,就导致了二义性。

菱形二义性的解决:

  1. 作用域限定符,‘’::‘’,明确的指出来,谁是谁的谁
  2. 在子类中定义同名的成员覆盖掉父类继承过来的成员。只是不报错了,相对应的,父类继承过来的成员也不能直接用了
  3. 虚继承:父类对祖父类的继承改为虚继承(virtual),各父类共享一个祖父类的拷贝,那么子类从父类继承过来的祖父类成员也就只有一个了。

菱形继承的解决代码举例:

#include "iostream"
using namespace std;

class A
{
public:
    A(int a) {}
    ~A() {}

    int m_a;
};

class B : virtual public A
{
public:
    B(int a, int b) : A(a) {}
    ~B() {}

    int m_b;
};

class C : virtual public A
{
public:
    C(int a, int c) : A(a) {}
    ~C() {}

    int m_c;
};

class D : public B, public C
{
public:
    D(int a, int b, int c, int d) : A(a), B(a, b), C(a, c)
    {
        m_a = a;
        m_b = b;
        m_c = c;
        m_d = d;
    }
    ~D() {}

    int m_d;
};

int main()
{
    D d(1, 2, 3, 4);

    cout << d.m_a << endl;
    cout << sizeof(d) << endl;

    cout << &d << endl;
    cout << &d.m_a << endl;
    cout << &d.m_b << endl;
    cout << &d.m_c << endl;
    cout << &d.m_d << endl;

    return 0;
}



类内属性的定义与继承后的属性变化
public protected private
public 公有属性在公有继承后,类内类外都可正常使用 公有属性在保护继承后,对于子类来说,变为保护属性,类外不可用 公有属性在私有继承后,对于子类来说,变为私有属性,类外不可用
protected 保护属性在公有继承后,维持其本属性,仍为保护属性,类外不可用 保护属性在保护继承后,类外仍不可用 保护属性在私有继承后,对于子类变为私有属性,类外不可用
private 对于子类来说,类内类外都不可用 对于子类来说,类内类外都不可用 对于子类来说,类内类外都不可用


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值