C++核心编程之继承

1 继承的基本语法

        在我们的生活中,很多事物都有一个最初的模板。比如,房子、车子等等,他们都有相关的房子胚型(屋顶+墙壁...)、车子图纸(框架+四个轮子+车灯...)。大家更着我的脚步,一步一个脚印。作为学者的我们,经常和学习网站打交道(bibilili、大学Mooc等),他们各自的网站,都有公共的头部、公共的底部等公共区域,只有一些中心内容随着我们搜索的内容而改变。

        接下来,我们分别用普通写法和继承写法来实现bibilili中的内容,我们可以从两者实现的代码区别来分析得到继承存在的意义和好处。

①普通写法:

// Java搜索页面
class Java 
{
public:
	void header()
	{
		cout << "首页、番剧、直播、游戏中心...(公共头部)" << endl;
	}
	void footer()
	{
		cout << "帮助中心、交流合作、站内地图...(公共底部)" << endl;
	}
	void content()
	{
		cout << "JAVA视频" << endl;
	}
};

// 游戏搜索页面
class Game
{
    public:
        void header()
        {
            cout << "首页、番剧、直播、游戏中心...(公共头部)" << endl;
        }
        void footer()
        {
            cout << "帮助中心、交流合作、站内地图...(公共底部)" << endl;
        }
        void content()
        {
            cout << "游戏Game视频" << endl;
        }
};

// 电视剧搜索页面
class Tv
{
    public:
        void header()
        {
            cout << "首页、番剧、直播、游戏中心...(公共头部)" << endl;
        }
        void footer()
        {
            cout << "帮助中心、交流合作、站内地图...(公共底部)" << endl;
        }
        void content()
        {
            cout << "电视剧视频" << endl;
        }
};

②继承写法:

#include
using namespace std;


// 继承实现页面
// 公共页面类
class BasePage
{
public:
    void header()
    {
        cout << "首页、番剧、直播、游戏中心...(公共头部)" << endl;
    }
    void footer()
    {
        cout << "帮助中心、交流合作、站内地图...(公共底部)" << endl;
    }

};

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

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

// 游戏页面
class Game:public BasePage
{
    public:
        void content()
        {
            cout << "游戏Game视频" << endl;
        }
};

// 电视剧页面
class Tv:public BasePage
{
    public:
        void content()
        {
            cout << "电视剧视频" << endl;
        }
};
void test01()
{
    cout<<"Java页面如下:"<

总结:

  • 继承的好处:可以减少重复的代码
class A : public B; 

A 类称为子类 或 派生类

B 类称为父类 或 基类

派生类中的成员,包含两大部分:

① 一类是从基类继承过来的;
② 一类是自己增加的成员。

从基类继承过过来的表现其共性,而新增的成员体现了其个性。

2 继承方式

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

继承方式:

继承方式 语法 特点
公共继承

class 子类 : public  父类

  • 父类中的公共权限成员 到 子类中依然是公共权限
  • 父类中的保护权限成员 到 子类中依然是保护权限
  • 父类中的私有权限成员 子类无法访问
保护继承 class 子类 : protected 父类
  • 父类中的公共成员,到子类中变为保护权限
  • 父类中的保护权限,到子类中仍为保护权限
  • 父类中的私有权限成员 子类无法访问
私有继承 class 子类 : private 父类
  • 父类中的公共成员,到子类中变为私有权限
  • 父类中的保护权限,到子类中变为私有权限
  • 父类中的私有权限成员 子类无法访问

代码案例如下:

#include
using namespace std;

// 继承方式

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

class Son1:public Basedata
{
public:
    void func()
    {
        m_A = 10;  // 父类中的公共权限成员 到 子类中依然是公共权限
        m_B = 10;  // 父类中的保护权限成员 到 子类中依然是保护权限
        // m_C = 10;  // 父类中的私有权限成员 子类无法访问
    }
};

void test01()
{
    Son1 s1;
    s1.m_A = 23;
    // s1.m_B = 24;  // 到Son1中 m_B是保护权限,类外访问不到
}

// 保护继承
class Son2:protected Basedata
{
public:
    void func()
    {
        m_A = 23;   // 父类中的公共成员,到子类中变为保护权限
        m_B = 24;   // 父类中的保护权限,到子类中仍为保护权限
        // m_C = 25;  // 父类中的私有权限成员 子类无法访问
    }
};

void test02()
{
    Son2 s2;
    // s2.m_A = 10; // 到Son2中 m_A变为保护权限,类外访问不到
    // s2.m_B = 10; // 到Son2中 m_B保护权限  类外访问不到
}

// 私有继承
class Son3:private Basedata
{
public:
    void func()
    {
        m_A = 23;  // 父类中的公共成员,到子类中变为私有权限
        m_B = 24;  // 父类中的保护权限,到子类中变为私有权限
        // m_C = 25;  // 父类中的私有权限成员 子类无法访问
    }
};

class GrandSon3:public Son3
{
public:
    void func()
    {
        // m_A = 100;  // 到了Son3中,m_A变为私有,即使是儿子,也是访问不到
        // m_B = 100;  // 到了Son3中,m_A变为私有,即使是儿子,也是访问不到
    }

};

int main()
{
    system("pause");
    return 0;
}

3 继承中的对象模型

        从父类继承过来的成员,父类中所有非静态成员属性都会被子类继承下去。

代码案例如下:

#include
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;

};

// 利用开发人员命令提示工具查看对象模型
// 路径调整到文件下路径
// cl /d1 reportSingleClassLayout类名 文件名

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

代码输出如下:

C++核心编程之继承_第1张图片

结论: 从本案例中,我们发现子类占用的内存为16,说明了父类中私有属性也被子类继承下去了,只是被编译器给隐藏了,因此访问不到其数据,但是确实是被继承下来了。

4 继承中构造和析构顺序

        子类继承父类后,当创建子类对象,也会调用父类的构造函数。但是我们会想到一个问题,就是父类和子类的构造和析构顺序到底是如何排的?

代码案例如下:

#include
using namespace std;

// 继承中的构造和析构顺序

class Base
{
public:
    Base()
    {
        cout<<"Base构造函数的调用"<

代码输出如下:

C++核心编程之继承_第2张图片

总结:继承中 先构造父类,再构造子类;析构的顺序与构造的顺序相反

5 继承同名成员处理方式

        当子类与父类出现同名的成员,我们通过以下方式来访问子类和父类中同名的成员和函数:

  • 访问子类同名成员   直接访问即可
  • 访问父类同名成员   需要加作用域

​​​​​​​代码案例如下:

#include
using namespace std;

// 继承同名成员处理方式

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

总结:

  1. 子类对象可以直接访问到子类中同名成员
  2. 子类对象加作用域可以访问到父类同名成员
  3. 如果子类中出现与父类同名的成员函数,子类的同名成员会隐藏掉父类中的所有同名成员函数
  4. 如果想访问到父类中被隐藏的同名成员函数,需要加作用域

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

        静态成员和非静态成员出现同名,处理方式是一致。其处理方式如下:

  • 访问子类同名成员   直接访问即可
  • 访问父类同名成员   需要加作用域

​​​​​​​代码案例如下:

#include
using namespace std;

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

class Base
{
public:
    static int m_A;

    static void func()
    {
        cout<<"static Base-func()调用"<

总结:同名静态成员处理方式和非静态成员处理方式一样,只不过有两种访问的方式(通过对象 访问和通过类名访问)

7 多继承语法

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

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

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

​​​​​​​代码案例如下:

#include
using namespace std;

// 多继承语法

class Base1
{
public:
    
    Base1()
    {
        m_A = 99;
    }

    int m_A;

};

class Base2
{
public:
    
    Base2()
    {
        m_A = 1;
    }

    int m_A;

};

// 子类   继承Base1 和 Base2
// 语法: class 子类: 继承方式 父类1, 继承方式 父类2...
class Son:public Base1,public Base2
{
public:
    Son()
    {
        m_C = 199;
        m_D = 288;
    }
    int m_C;
    int m_D;
    
};


void test01()
{
    Son s1;

    cout<<"size of Son = "<< sizeof(s1)<

代码输出如下:

C++核心编程之继承_第3张图片

总结:在C++实际开发中,不建议用多继承语法的。而且,在多继承中,如果父类中出现了同名情况,则子类使用的时候一定要加对应的作用域哦。

8 菱形继承

        概念:两个派生类继承同一个基类,又有某个类同时继承者两个派生类,这种继承被称为菱形继承(钻石继承)。

        案例:羊和驼继承同一个基类(动物),又有羊驼同时继承者两个派生类(羊和驼)

        问题:

  1. 羊继承了动物的数据,驼同样继承了动物的数据,当羊驼使用数据时,就会产生二义性。
  2. 羊驼继承自动物的数据继承了两份,但是这份数据只需要一份就可以。

​​​​​​​代码案例如下:

#include
using namespace std;

// 菱形继承
// 动物类
class Animal
{
public:
    int m_Age;

};

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

// 驼类 
class Tuo:virtual public Animal{};

// 羊驼类
class YangTuo:public Sheep, public Tuo{};

void test01()
{
    YangTuo yt;

    yt.Sheep::m_Age = 20;
    yt.Tuo::m_Age = 24;
    // 当菱形继承,两个父类拥有相同数据,需要加以作用域区分
    cout<<"yt.Sheep::m_Age ="<

案例知识点:

  • 利用虚继承 解决菱形继承的问题
  • 继承之前 加上关键字 virtual 变为虚继承
  • Animal类称为 虚基类

总结:

  • 菱形继承带来的主要问题是子类继承两份相同的数据,导致资源浪费
  • 利用虚继承virtual可以解决菱形继承问题


 

你可能感兴趣的:(嵌入式学习之C++,c++,开发语言,笔记,学习)