【贪玩巴斯】c++核心编程,你需要敲出这些程序 !(六){继承 的详解!} //2021-05-03

//

//  main.cpp

//  _day6

//

//  Created by AchesonD16 贪玩巴斯 on 2021/4/23.

//

 

 

 

//继承

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

//

//有些类与类之间存在特殊的关系,例如:

【贪玩巴斯】c++核心编程,你需要敲出这些程序 !(六){继承 的详解!} //2021-05-03_第1张图片

 

//我们发现,定义这些类时,下一级别的成员除了拥有上一级的共性,并且还拥有自己的特性

//这个时候我们可以考虑利用继承的技术,减少重复代码

 

 

 

1、继承的基本语法

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

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

 

1、1 普通实现

 

#include

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;

    }

};

 

//Python页面

class Python

{

public:

    void header()

    {

        cout << "首页、公开课、登录、注册...(公共头部)" << endl;

    }

    void footer()

    {

        cout << "帮助中心、交流合作、站内地图...(公共底部)" << endl;

    }

    void left()

    {

        cout << "Java,Python,c++...(公共分类列表)" << endl;

    }

    void content()

    {

        cout << "Python学科视频" << endl;

    }

};

 

//c++页面

class Cpp

{

public:

    void header()

    {

        cout << "首页、公开课、登录、注册...(公共头部)" << endl;

    }

    void footer()

    {

        cout << "帮助中心、交流合作、站内地图...(公共底部)" << endl;

    }

    void left()

    {

        cout << "Java,Python,c++...(公共分类列表)" << endl;

    }

    void content()

    {

        cout << "c++学科视频" << endl;

    }

};

 

void test1()

{

    //Java页面

    cout << "Java下载视频页面如下:" << endl;

    java ja;

    ja.header();

    ja.footer();

    ja.left();

    ja.content();

    cout << "---------------------" << endl;

    

    //Python页面

    cout << "Python下载视频页面如下:" << endl;

    Python py;

    py.header();

    py.footer();

    py.left();

    py.content();

    cout << "---------------------" << endl;

 

    //c++页面

    cout << "c++下载视频页面如下:" << endl;

    Cpp c;

    c.header();

    c.footer();

    c.left();

    c.content();

    cout << "---------------------" << endl;

}

 

int main()

{

    test1();

    

    system("pause");

    return 0;

}

*/

 

 

1、2 使用继承实现

 

/*

#include

using namespace std;

 

//公共页面

class BasePage

{

public:

    void header()

    {

        cout << "首页、公开课、登录、注册...(公共头部)" << endl;

    }

    void footer()

    {

        cout << "帮助中心、交流合作、站内地图...(公共底部)" << endl;

    }

    void left()

    {

        cout << "Java,Python,c++...(公共分类列表)" << endl;

    }

};

 

 

//Java页面   //固定格式 class 子类(派生类) : 继承方式 父类(基类)

class Java : public BasePage  //继承了父类的所有属性行为。

{

public:                       //同时具有自己的个性——自己的行为和属性

    void content()

    {

        cout << "JAVA学科视频" << endl;

    }

};

//Python页面

class Python : public BasePage

{

public:

    void content()

    {

        cout << "Python学科视频" << endl;

    }

};

 

class Cpp :  public BasePage

{

public:

    void content()

    {

        cout << "c++学科视频" << endl;

    }

};

 

void test1()

{

    //Java页面

    cout << "Java下载视频页面如下:" << endl;

    Java ja;

    ja.header();

    ja.footer();

    ja.left();

    ja.content();

    cout << "------------------" << endl;

    

    //Python页面

    cout << "Python下载视频页面如下: " << endl;

    Python py;

    py.header();

    py.footer();

    py.left();

    py.content();

    cout << "--------------------" << endl;

 

    //C++页面

    cout << "C++下载视频页面如下: " << endl;

    Cpp cp;

    cp.header();

    cp.footer();

    cp.left();

    cp.content();

}

 

 

int main()

{

    test1();

    

    system("pause");

    return 0;

}

 

 

1、3 总结和语法:

继承的好处:可以减少重复的代码

class A : public B

{};

A 类称为子类 或 派生类

B 类称为父类 或 基类

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

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

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

*/

 

 

2、继承方法

/*

//继承方式

//继承的语法:

class 子类 :继承方式 父类

//

//继承方式一共有三种:

//公共继承

//保护继承

//私有继承

//

【贪玩巴斯】c++核心编程,你需要敲出这些程序 !(六){继承 的详解!} //2021-05-03_第2张图片

 

#include

using namespace std;

 

class Base1

{

public:

    int m_A;

protected:

    int m_B;

private:

    int m_C;

};

 

//公共继承 public

class Son1 : public Base1

{

public:

    void func()

    {

        m_A = 10; //可访问 public权限

        m_B = 12; //可访问 protected权限

        //m_C = 4;  //不可访问 为父类的私有属性

    }

};

 

void myClass(int a)

{

    Son1 s1;

    s1.m_A = a;  //在类外只能调用公共类

    cout << s1.m_A << endl;

}

 

//保护继承 protected

class Base2

{

public:

    int m_A;

protected:

    int m_B;

private:

    int m_C;

};

 

class Son2 : protected Base2

{

public:

    void func()

    {

        m_A = 5; //可访问 protected权限

        m_B = 55; //可访问 protected权限

        //m_C = 23; //不可访问,其为父类的私有属性

    }

};

 

void myClass2()

{

    Son2 s2;

    s2.func();

    //s2.m_A; 是不可以访问的,因为其继承过来为 protected权限

}

 

//私有继承

class Base3

{

public:

    int m_A;

protected:

    int m_B;;

private:

    int m_C;

    

};

 

class Son3:private Base3

{

public:

    void func()

    {

        m_A = 10;  //可访问 private权限

        m_B = 23;  //可访问 private权限

        //m_C;     不可以访问

    }

};

 

class GrandSon3:public Son3

{

public:

    void func()

    {

        // 验证Son3的继承为私有继承,从Base3继承过来的皆为私有属性

        //m_A = m_B = 2; //不可以

    }

};

 

int main()

{

    myClass(2);

    myClass2();  //什么都不显示,因为是protected权限,出了类无法访问

    

    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;

};

 

void test1()

{

    cout << "sizeof Son = " << sizeof(Son) << endl;

}

 

int main()

{

    test1();

    

    system("pause");

    return 0;

}

结果:

sizeof Son = 16

结论:

父类中私有成员也是被子类继承下去了, 只是由编译器给隐藏后访问不到

 

// 使用 程序员命令管理符来查看

【贪玩巴斯】c++核心编程,你需要敲出这些程序 !(六){继承 的详解!} //2021-05-03_第3张图片

 

打开工具窗口后,定位到当前CPP文件的盘符

然后输入: cl /d1 reportSingleClassLayout查看的类名 所属文件名

效果如下图:

【贪玩巴斯】c++核心编程,你需要敲出这些程序 !(六){继承 的详解!} //2021-05-03_第4张图片

*/

 

 

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

/*

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

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

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

 

#include

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 test1()

{

    //继承中 先调用父类构造函数,再调用子类构造函数, 析构顺序与构造相反,(栈先进后出) —— 先有爸爸后有儿子

    Son s;

}

 

int main()

{

    test1();

    

    system("pause");

    return 0;

}

结果:

Base构造函数!

Son构造函数!

Son析构函数!

Base析构函数!

 

总结:

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

*/

 

 

5、继承中同名成员的处理方式

/*

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

 

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

// 访问子类同名成员 直接访问即可

// 访问父类同名成员 需要加作用域

 

#include

using namespace std;

 

class Base  //创建基类、父类

{

public:

    Base()

    {

        m_A = 100;  //创建同名成员

    }

     

    void func()   //同名函数

    {

        cout << "Base - func()调用" << endl;

    }

    

    void func(int a) //函数重载

    {

        cout << "Base -func(int a)调用" << endl;

    }

    

public:

    int m_A;

};

 

class Son:public Base  //创建子类,继承父类

{

public:

    Son()

    {

        m_A = 200;  // 同名

    }

    

    //当子类与父类拥有同名的成员函数,子类会隐藏父类中所有版本的同名成员函数

    //如果想访问父类中被隐藏的同名成员函数,需要加父类的作用域

    void func()  // 同名

    {

        cout << "Son - func()调用" << endl;

    }

public:

    int m_A;

};

 

void test1()

{

    Son s;

    

    cout << "Son下的m_A = " << s.m_A << endl;

    cout << "Base下的m_A = " << s.Base::m_A << endl; // 加作用域可以访问

    

    s.func();

    s.Base::func();   // 加 父类作用域

    s.Base::func(10); // 加 作用域

}

 

int main()

{

    test1();

    

    system("pause");

    return 0;

}

结果:

Son下的m_A = 200

Base下的m_A = 100

Son - func()调用

Base - func()调用

Base -func(int a)调用

 

总结:

1、子类对象可以直接访问到子类中同名的成员。

2、子类对象加作用域可以访问到父类同名成员。

3、当子类与父类拥有同名的成员函数,子类会隐藏父类中同名成员函数,加作用域才可以访问到父类中同名函数

*/

 

 

 

 

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

/*

  

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

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

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

// 1、访问子类同名成员 直接访问即可

// 2、访问父类同名成员 需要加作用域

 

#include

using namespace std;

 

class Base

{

public:

    static void func() // 构造静态成员函数

    {

        cout << "Base - static void func()" << endl;

    }

    static void func(int a)

    {

        cout << "Base - static void func(int a)" << endl;

    }

    

    static int m_A; //静态成员变量 , 类内定义,类外初始化

};

 

int Base::m_A = 100;

 

class Son : public Base   // 构造派生类,子类

{

public:

    static void func()  // 同名

    {

        cout << "Son - static void func()" << endl;

    }

    static int m_A;  // 同名

};

int Son::m_A = 200;  // 记得加作用域在中间

 

//同名成员属性

void test1()

{

    // 通过对象访问

    cout << "通过对象访问:"  << endl;

    Son s;

    cout << "Son 下 m_A = " << s.m_A << endl;

    cout << "Base下 m_A = " << s.Base::m_A << endl;   // 加作用域访问父类中的同名静态成员变量

    

    // 通过类名访问

    cout << "通过类名访问: " << endl;

    cout << "Son 下 m_A = " << Son::m_A << endl;

    cout << "Base下 m_A = " << Son::Base::m_A << endl;

}

 

//同名成员函数

void test2()

{

    //通过对象访问

    cout << "通过对象访问:" << endl;

    Son s;

    s.func();

    s.Base::func();  // 加作用域以访问父类中同名静态成员函数

    

    cout << "通过类名访问:" << endl;

    Son::func();

    Son::Base::func();

    //出现同名,子类会隐藏父类中所有同名成员函数,需要加作用域访问

    Son::Base::func(100);

}

int main()

{

    test1();

    test2();

    

    system("pause");

    return 0 ;

}

结果:

通过对象访问:

Son 下 m_A = 200

Base下 m_A = 100

通过类名访问:

Son 下 m_A = 200

Base下 m_A = 100

通过对象访问:

Son - static void func()

Base - static void func()

通过类名访问:

Son - static void func()

Base - static void func()

Base - static void func(int a)

 

总结:

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

*/

 

 

 

 

 

7、多继承语法

/*

// 多继承语法

// c++允许一个类继承多个父类

// 语法:class 子类 :继承方式 父类1, 继承方式 父类2 ... {};

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

// 注意: c++实际开发中不建议用多继承

 

#include

using namespace std;

 

class Base1

{

public:

    Base1()

    {

        m_A = 100;

    }

    

public:

    int m_A;

};

 

class Base2

{

public:

    Base2()

    {

    m_A = 200;   // 如果同名会 出现不明确

    }

public:

    int m_A;

};

 

// 多继承语法: class 子类: 继承方式 父类1, 继承方式2 父类2, ...{};

class Son : public Base1, public Base2

{

public:

    Son()

    {

        m_C = 300;

        m_D = 400;

    }

public:

    int m_C;

    int m_D;

};

 

 

// 多继承容易产生成员同名的情况

// 通过使用类名作用域可以区分调用哪一个基类的成员

void test1()

{

    Son s;

    cout << "sizeof Son = " << sizeof(s) << endl;

    cout << s.Base1::m_A << endl;

    cout << s.Base2::m_A << endl;

}

 

int main()

{

    test1();

    

    system("pause");

    return 0;

}

结果:

sizeof Son = 16

100

200

 

总结:

多继承中如果多个父类中出现了同名情况,子类使用的时候要加作用域

*/

 

 

 

 

8、菱形继承

// 菱形继承

/*

菱形继承的概念:

两个派生类继承同一个基类

又有某个类同时继承着这两个派生类

构成菱形◇

这种继承被称为 菱形继承, 或者 钻石继承

 

典型的菱形继承案例:

【贪玩巴斯】c++核心编程,你需要敲出这些程序 !(六){继承 的详解!} //2021-05-03_第5张图片

 

菱形继承问题:

羊继承了动物,驼同样继承了动物的数据,草泥马同时继承了驼类,羊类,那么当草泥马使用当中的数据时,就会产生二义性

草泥马继承自动物的数据继承了两份,其实我们应该很清楚,这份数据我们只需要一份就可以了。

 

 

#include

using namespace std;

 

class Animal

{

public:

    int m_Age;

};

 

//解决继承两份数据的方法 : 继承前加 virtual 关键字后, 变为虚继承

//此时公共的最大的父类Animal称为 虚基类

class Sheep : virtual public Animal

{};

class Tuo : virtual public Animal

{};

class SheepTuo : public Sheep, public Tuo

{};

 

void test1()

{

    SheepTuo st;  // 创建羊驼的对象

    st.Sheep::m_Age = 100; //访问的是羊作用域下继承的父类的m_Age

    st.Tuo::m_Age = 200;   //访问的是驼作用域下继承的父类的m_Age

    

    cout << "st.Sheep::m_Age = " << st.Sheep::m_Age << endl;

    cout << "st.Tuo::m_Age = " << st.Tuo::m_Age << endl;

    cout << "st.m_Age = " << st.m_Age << endl;

}

 

int main()

{

    test1();

    

    system("pause");

    return 0;

}

结果:

st.Sheep::m_Age = 200

st.Tuo::m_Age = 200

st.m_Age = 200

 

总结:

使用了虚继承,那么最后赋值的为最终的值的结果

菱形继承带来的问题主要是子类继承两份相同的数据,导致了资源浪费以及毫无意义

利用虚继承可以解决菱形继承问题!!

*/

你可能感兴趣的:(C++初学笔记(入门,核心,进阶),类,编程语言,c++)