C++学习(13)—— 继承

继承是面向对象的三大特性之一

1.继承的基本语法

例如我们看到很多网站中,都有公共的头部,公共的底部,甚至公共的左侧列表,只有中心内容不同

接下来我们分别利用普通写法和继承的写法来实现网页中的内容,看一下继承存在的意义以及好处

  • 继承的好处:减少重复代码
  • 语法class 子类 : 继承方式 父类
  • 子类也称为派生类父类也称为基类

普通实现:

#include<iostream>
using namespace std;

//普通实习页面
class Java{
public:
    void header(){
        cout << "首页、公开课、登陆、注册...(公共头部)" << endl;
    }
    void footer(){
        cout << "帮助中心、交流合作、站内地图...(公共底部)" << endl;
    }
    void left(){
        cout << "Java、Python、C++...(公共分类列表)" <<endl;
    }
    void content(){
        cout << "Java学科视频" << endl;
    }
};

class Python{
public:
    void header(){
        cout << "首页、公开课、登陆、注册...(公共头部)" << endl;
    }
    void footer(){
        cout << "帮助中心、交流合作、站内地图...(公共底部)" << endl;
    }
    void left(){
        cout << "Java、Python、C++...(公共分类列表)" <<endl;
    }
    void content(){
        cout << "Python学科视频" << endl;
    }
};

void test01(){
    cout << "Java下载视频页面如下:" << endl;
    Java ja;
    ja.header();
    ja.footer();
    ja.left();
    ja.content();
}

void test02(){
    cout << "Python下载视频页面如下:" << endl;
    Python py;
    py.header();
    py.footer();
    py.left();
    py.content();
}

int main(){
    test01();
    cout << "------------------------------" << endl;
    test02();
    return 0;
}

继承实现:

#include<iostream>
using namespace std;

//公共页面
class BasePage{
public:
    void header(){
        cout << "首页、公开课、登陆、注册...(公共头部)" << endl;
    }
    void footer(){
        cout << "帮助中心、交流合作、站内地图...(公共底部)" << endl;
    }
    void left(){
        cout << "Java、Python、C++...(公共分类列表)" <<endl;
    }
};

//Java页面
class Java : public BasePage{
public:
    void content(){
        cout << "Java学科视频" << endl;
    }
};

//Python页面
class Python : public BasePage{
public:
    void content(){
        cout << "Python学科视频" << endl;
    }
};

void test01(){
    cout << "Java下载视频页面如下:" << endl;
    Java ja;
    ja.header();
    ja.footer();
    ja.left();
    ja.content();
}

void test02(){
    cout << "Python下载视频页面如下:" << endl;
    Python py;
    py.header();
    py.footer();
    py.left();
    py.content();
}

int main(){
    test01();
    cout << "------------------------------" << endl;
    test02();
    return 0;
}

2.继承方式

语法:class 子类 : 继承方式 父类

继承方式一共有三种:

  • 公共继承
  • 保护继承
  • 私有继承

#include<iostream>
using namespace std;

//公共继承
class Base1{
public:
    int m_A;
protected:
    int m_B;
private:
    int m_C;
};

class Son1 : public Base1{
public:
    void func(){
        m_A = 10; 
        m_B = 10;  //父类中保护成员,到子类中变为保护权限
        //m_C = 10;
    }
}

//保护继承
class Son2 : protected Base1{
    public:
    void func(){
        m_A = 10; //父类中公共成员,到子类中变为保护权限
        m_B = 10;
        //m_C = 10;
    }
}

//私有继承
class Son3 : protected Base1{
    public:
    void func(){
        m_A = 10; //父类中公共成员,到子类中变为保护权限
        m_B = 10;
        //m_C = 10;
    }
}

void test01(){
    Son1 s1;
    s1.m_A = 100;
    //s1.m_B = 100;
    //s1.m_C = 100;
}

void test02(){
    Son2 s2;
    s2.m_A = 100;
    //s2.m_B = 100;
    //s2.m_C = 100;
}

void test03(){
    Son3 s3;
    s3.m_A = 100;
    //s3.m_B = 100;
    //s3.m_C = 100;
}

int main(){
    test01();
    return 0;
}

3.继承中的对象模型

问题:从父类继承过来的成员,哪些属于子类对象中?

#include<iostream>
using namespace std;

class Base{
public:
    int m_A;
protected:
    int m_B;
private:
    int m_C;
};

class Son : public Base{
public:
    int m_D;
};

void test01(){
    //父类中所有非静态成员属性都会被子类继承下去
    //父类中私有成员属性,是被编译器给隐藏了,因此是访问不到的,但是确实被继承下去了
    cout << "size of Son = " << sizeof(Son) << endl;
}

int main(){
    test01();
    return 0;
}

4.继承中构造和析构顺序

子类继承父类后,当创建子类对象,也会调用父类构造函数

问题:父类和子类的构造和析构顺序是谁先谁后呢?

#include<iostream>
using namespace std;

class Base{
public:
    Base(){
        cout << "Base构造函数" << endl;
    }
    ~Base(){
        cout << "Base析构函数" << endl;
    }
};

class Son : public Base{
public:
    Son(){
        cout << "Son构造函数" << endl;
    }
    ~Son(){
        cout << "Son析构函数" << endl;
    }
};

void test01(){
    Base b;
    Son s;
}

int main(){
    test01();
    return 0;
}

总结:继承中,先调用父类构造函数,再调用子类构造函数,析构顺序与构造相反

5.继承同名成员处理方式

问题:当子类与父类出现同名成员,如何通过子类对象,访问到子类或父类中同名的数据呢?

  • 访问子类同名成员 直接访问即可
  • 访问父类同名成员 需要加作用域
#include<iostream>
using namespace std;

class Base{
public:
    Base(){
        m_A = 100;
    }
    void func(){
        cout << "Base - func()调用 " << endl;
    }
    int m_A;
};

class Son : public Base{
public:
    Son(){
        m_A = 200;
    }
    void func(){
        cout << "Son - func()调用 " << endl;
    }
    int m_A;
};

void test01(){
    Son s;
    cout << "m_A = " << s.Base::m_A << endl;
    s.Base::func();
}

int main(){
    test01();
    return 0;
}

总结:

  1. 子类对象可以直接访问到子类中的同名成员
  2. 子类对象加作用域可以访问到父类同名成员
  3. 当子类与父类拥有同名成员函数,子类会隐藏父类中同名成员函数,加作用域可以访问到父类中同名函数

6.继承同名静态成员处理方式

问题:继承中同名的静态成员在子类对象上如何进行访问?

静态成员和非静态成员出现同名,处理方式一致

  • 访问子类同名成员 直接访问即可
  • 访问父类同名成员 需要加作用域
#include<iostream>
using namespace std;

//继承中的同名静态成员处理方式

class Base{
public:
   static int m_A;
};
int Base::m_A = 100;

class Son : public Base{
public:
    static int m_A;
};
int Son::m_A = 200;

void test01(){
    //1.通过对象访问数据
    Son s;
    cout << "通过对象访问数据" << endl;
    cout << "Son下m_A = " << s.m_A << endl;
    cout << "Base下m_A = " << s.Base::m_A << endl;
    //2.通过类名访问数据
    cout << "通过类名访问数据" << endl;
    cout << "Son下m_A = " << Son::m_A << endl;
    cout << "Base下m_A = " << Son::Base::m_A << endl;
}

int main(){
    test01();
    return 0;
}

7.多继承语法

C++允许一个类继承多个类

语法:class 子类 : 继承方式 父类1,继承方式 父类2...

多继承可能会引发父类中有同名成员出现,需要加作用域区分

C++实际开发中不建议使用多继承

8.菱形继承

菱形继承概念:

  • 两个派生类继承同一个基类
  • 又有某个类同时继承两个派生类
  • 这种继承被称为菱形继承,或者钻石继承

典型的菱形继承案例:

    动物

    ↓       ↘

    羊        驼

    ↓       ↙

    羊驼

菱形继承问题:

  • 羊继承了动物的数据,驼同样继承了动物的数据,当羊驼使用数据时,就会产生二义性
  • 羊驼继承自动物的数据继承了两份,其实我们清楚,这份数据我们只需要一份就可以
#include<iostream>
using namespace std;

//动物类
class Animal{
public:
   int m_A;
};

//利用虚继承解决菱形继承的问题
//继承之前加上关键字virtual变成虚继承
//Animal类称为 虚基类
class Sheep : virtual public Animal{
};

class Tuo : virtual public Animal{
};

class SheepTuo : public Sheep, public Tuo{
};

void test01(){
    SheepTuo st;
    st.Sheep::m_A = 18;
    st.Tuo::m_A = 28;
    //当菱形继承时,两个父类拥有相同数据,需要加以作用域区分
    cout << "st.Sheep::m_A = " << st.Sheep::m_A << endl;
    cout << "st.Tuo::m_A = " << st.Tuo::m_A << endl;
    cout << "st.m_A = " << st.m_A << endl;

    //这份数据我们知道,只要有一份就可以,菱形继承导致数据有两份
}

int main(){
    test01();
    return 0;
}

虚继承:

  • 继承指针,生成对应得偏移量表,指向唯一数据

总结:

  • 菱形继承带来的主要问题是子类继承两份相同得数据,导致资源浪费以及毫无意义
  • 利用虚继承可以解决菱形继承问题
posted @ 2019-12-23 19:07  马尔尤尤  阅读(225)  评论(0编辑  收藏  举报