继承

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

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

 

 

 

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

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

4.6.1 继承的基本语法

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

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

  1 #include <iostream>
  2 using namespace std;
  3 //普通实现
  4 //java页面
  5 //class java
  6 //{
  7 //public:
  8 //    void header()
  9 //    {
 10 //        cout << "首页,公开课...(公共头部)" << endl;
 11 //    }
 12 //    void footer()
 13 //    {
 14 //        cout << "帮助中心,交流合作 ...(公共底部)" << endl;
 15 //    }
 16 //    void left()
 17 //    {
 18 //        cout << "JAVA ,python,c/c++(公共分类列表)" << endl;
 19 //    }
 20     //void content()
 21     //{
 22     //    cout << "java学科视频" << endl;
 23     //}
 24 //};
 25 ////python页面
 26 //class python
 27 //{
 28 //public:
 29 //    void header()
 30 //    {
 31 //        cout << "首页,公开课...(公共头部)" << endl;
 32 //    }
 33 //    void footer()
 34 //    {
 35 //        cout << "帮助中心,交流合作 ...(公共底部)" << endl;
 36 //    }
 37 //    void left()
 38 //    {
 39 //        cout << "JAVA ,python,c/c++(公共分类列表)" << endl;
 40 //    }
 41 //    void content()
 42 //    {
 43 //        cout << "python学科视频" << endl;
 44 //    }
 45 //};
 46 
 47 //继承实现页面
 48 //继承好处:减少重复代码
 49 //语法 : class 子类(派生类) :继承方式 父类(基类)
 50 class basepage //公共信息
 51 {
 52 public:
 53    void header()
 54         {
 55             cout << "首页,公开课...(公共头部)" << endl;
 56         }
 57    void footer()
 58         {
 59             cout << "帮助中心,交流合作 ...(公共底部)" << endl;
 60         }
 61    void left()
 62         {
 63             cout << "JAVA ,python,c/c++(公共分类列表)" << endl;
 64         }
 65 };
 66 class java : public basepage
 67 {
 68 public:
 69     void content()
 70     {
 71         cout << "java学科视频" << endl;
 72     }
 73 };
 74 class python : public basepage
 75 {
 76 public:
 77     void content()
 78     {
 79         cout << "python学科视频" << endl;
 80     }
 81 };
 82 void test01()
 83 {
 84     cout << "java 下载视频界面如下:" << endl;
 85     java ja;
 86     ja.header();
 87     ja.footer();
 88     ja.left();
 89     ja.content();
 90     cout << "---------------------" << endl;
 91     cout << "python 下载视频界面如下:" << endl;
 92     python py;
 93     py.header();
 94     py.footer();
 95     py.left();
 96     py.content();
 97 
 98 }
 99 int main()
100 {
101     test01();
102 }

 

 

 

总结:

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

class A : public B;

A 类称为子类 或 派生类

B 类称为父类 或 基类

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

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

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

 

继承方式

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

继承方式一共有三种:

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

 

 

 

 1 #include <iostream>
 2 using namespace std;
 3 //继承方式
 4 class base1
 5 {
 6 public:
 7     int m_a;
 8 protected:
 9     int m_b;
10 private:
11     int m_c;
12 };
13 //公共继承
14 class son1:public base1
15 {
16 public:
17     void fuc()
18     {
19         m_a = 10;//父类中的公共权限成员,到子类中依然是公共权限
20         m_b = 20;//父类中的保护权限成员,到子类中依然是保护权限
21         //m_c = 30;////父类中的私有权限成员,子类访问不到
22     }
23 };
24 void test01()
25 {
26     son1 s1;
27     s1.m_a = 100;
28     //s1.m_b = 100;//m_b是保护权限类外不可访问
29 }
30 //保护继承
31 class son2 :protected base1
32 {
33     void fuc2()
34     {
35         m_a = 10;//父类中的公共权限成员,通过保护继承到子类中是保护权限
36         m_b = 20; //父类中的保护权限成员,到子类中依然是保护权限
37         //m_c = 30;////父类中的私有权限成员,子类访问不到
38     }
39 };
40 void test02()
41 {
42     son2 s2;
43     //s2.m_a = 100;//m_b是保护权限类外不可访问
44     //s1.m_b = 100;//m_b是保护权限类外不可访问
45 }
46 //私有继承
47 class son3 :private base1
48 {
49     void fuc3()
50     {
51         m_a = 10;//父类中的公共权限成员,通过保护继承到子类中是私有权限,子类可以访问
52         m_b = 20; //父类中的保护权限成员,通过保护继承到子类中是私有权限,子类可以访问
53        // m_c = 30;////父类中的私有权限成员,子类访问不到
54     }
55 };
56 void test03()
57 {
58     son3 s3;
59     // s2.m_a = 100;//m_b是私有权限类外不可访问
60     //s1.m_b = 100;//m_b是私有权限类外不可访问
61 }
62 class grandson3 :public son3
63 {
64 public:
65     void func()
66     {
67        // m_a = 10;//到了son3中m_a变为私有内容,即使是继承给别的类依然是私有内容
68        // m_b = 20; 
69        // m_c = 30;
70     }
71 
72 };
73 int main()
74 {
75 
76 }

继承中的对象模型

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

 1 #include <iostream>
 2 using namespace std;
 3 class base
 4 {
 5 public:
 6     int m_a;
 7 protected:
 8     int m_b;
 9 private:
10     int m_c;
11 };
12 class son : public base
13 {
14 public :
15     int m_d;
16 };
17 //利用开发人员命令工具提示工具查看对象模型
18 //跳转盘符到程序所在盘,比如在F盘就打 F:然后回车,如果本身就在这个盘了就不用跳转了
19 //跳转文件路径 输入 cd 具体的文件地址
20 //查看命名 dir
21 //cl /d1 reportSingleClassLayout类目 文件名,如cl /d1 reportSingleClassLayoutson "继承中的对象模型.cpp"
22 void test01()
23 {
24     //在父类中所有的非静态属性都会被子类继承下去
25     //父类中私有的成员属性,是被编译器隐藏了,但是确实被继承了下去
26     cout << "son占内存空间大小为:" << sizeof(son) << endl;
27 }
28 int main()
29 {
30     test01();
31 }

 

 

 

 

 

 

 

 

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

继承中构造和析构顺序

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

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

 1 #include <iostream>
 2 using namespace std;
 3 // 继承中构造和析构顺序
 4 class base
 5 {
 6 public:
 7     base()
 8     {
 9         cout << "base 中构造函数" << endl;
10     }
11     ~base()
12     {
13         cout << "base 中析构函数" << endl;
14     }
15 };
16 class son :public base
17 {
18 public:
19     son()
20     {
21         cout << "son 中构造函数" << endl;
22     }
23     ~son()
24     {
25         cout << "son 中析构函数" << endl;
26     }
27 };
28 void test01()
29 {
30     //继承中的构造与析构顺序如下
31     //继承中 先调用父类构造函数,再调用子类构造函数,析构顺序与构造相反
32     //base b;
33     son so;
34 }
35 int main()
36 {
37     test01();
38 }

 

 

继承同名成员处理方式

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

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

 

 1 #include <iostream>
 2 using namespace std;
 3 class base
 4 {
 5 public:
 6     base()
 7     {
 8          m_a = 100;
 9     }
10     void func()
11     {
12         cout << "base 下func函数的调用" << endl;
13     }
14     void func(int a)
15     {
16         cout << "base 下func(int a)的调用" << endl;
17     }
18     int m_a;
19 };
20 class son : public base
21 {
22 public:
23     son()
24     {
25         m_a = 200;
26     }
27     void func()
28     {
29         cout << "son 下func函数的调用" << endl;
30     }
31     int m_a;
32 };
33 //同名成员属性处理方式
34 void test01()
35 {
36     son s;
37     cout << "son  下的 m_a = " << s.m_a << endl;
38     //子类对象加作用域可以访问到父类同名成员
39     cout << "base 下的 m_a = " << s.base::m_a << endl;
40 }
41 //同名函数处理方式
42 void test02()
43 {
44     son s;
45     s.func();//直接调用子类中的同名成员
46     //调用父类下的同名函数
47     s.base::func();
48     //当子类与父类拥有同名的成员函数,子类会隐藏父类中同名成员函数
49     //加作用域可以访问到父类中同名函数
50     s.base::func(100);
51 }
52 int main()
53 {
54     //test01();
55     test02();
56 }

 

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

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

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

  • 访问子类同名成员 直接访问即可
  • 访问父类同名成员 需要加作用域
 1 #include <iostream>
 2 using namespace std;
 3 class base
 4 {
 5 public:
 6     static int m_a;//编译阶段分配内存,所有对象共享同一份数据;类内声明类外初始化
 7     static void func()
 8     {
 9         cout << "base - static void func()" << endl;
10     }
11     static void func(int a)
12     {
13         cout << "base - static void func(int a)" << endl;
14     }
15 };
16 int base::m_a = 100;
17 class son :public base
18 {
19 public:
20     static int m_a;
21     static void func()
22     {
23         cout << "son - static void func()" << endl;
24     }
25 };
26 int son::m_a = 200;
27 //同名静态成员属性
28 void test01()
29 {
30     //通过对象访问
31     cout << "通过对象访问" << endl;
32     son s;
33     cout << "son下m_a = " << s.m_a << endl;
34     cout << "base下m_a = " << s.base::m_a << endl;
35     //通过类名进行访问
36     cout << "通过类名进行访问" << endl;
37     cout << "son下m_a = " << son::m_a << endl;
38     cout << "base下m_a = " << son::base::m_a << endl;//第一个::,是通过son类名进行访问base::m_a,第二个::代表访问父类作用域下
39 
40 }
41 //同名静态成员函数
42 void test02()
43 {
44     //通过对象访问
45     cout << "通过对象访问" << endl;
46     son s;
47     s.func();
48     s.base::func();
49     //通过类名进行访问
50     cout << "通过类名进行访问" << endl;
51     son::func();
52     son::base::func();
53     //子类出现了和父类同名的静态成员函数,也会隐藏掉父类中所有同名成员函数
54     //如果想访问父类中被隐藏的同名成员,需要加父类的作用域
55     son::base::func(100);
56 }
57 
58 int main()
59 {
60     //test01();
61     test02();
62 }

 

 

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

多继承语法

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

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

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

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

 1 #include <iostream>
 2 using namespace std;
 3 //多继承基础语法
 4 class base1
 5 {
 6 public:
 7     base1()
 8     {
 9         m_a = 100;
10     }
11     int m_a;
12 };
13 class base2
14 {
15 public:
16     base2()
17     {
18         m_a= 200;
19     }
20     int m_a;
21 };
22 //子类 需要继承base1 base2
23 class son :public base1, public base2
24 {
25 public:
26     son()
27     {
28         m_c = 300;
29         m_d = 400;
30     }
31     int m_c;
32     int m_d;
33 };
34 void test01()
35 {
36     son s;
37     cout << "sizeof son =" << sizeof(s) << endl;
38     //当父类中出现同名成员需要加作用域区分
39     cout << "m_a = " << s.base1::m_a << endl;
40     cout << "m_a = " << s.base2::m_a << endl;
41 }
42 int main()
43 {
44     test01();
45 }

 

 

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

菱形继承

菱形继承概念:

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

​ 又有某个类同时继承者两个派生类

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

典型的菱形继承案例:

  1. 羊继承了动物的数据,驼同样继承了动物的数据,当草泥马使用数据时,就会产生二义性。
    
  2. 草泥马继承自动物的数据继承了两份,其实我们应该清楚,这份数据我们只需要一份就可以。

 

 

 菱形继承问题:

 1 #include <iostream>
 2 using namespace std;
 3 //动物类
 4 class animal
 5 {
 6 public:
 7     int m_age;
 8 };
 9 //利用虚继承 解决菱形继承的问题
10 // 继承之前加上关键字 virtual 变为虚继承
11 // 变成虚继承后animal类称为虚基类
12 //羊类
13 class sheep:virtual public animal
14 {
15 
16 };
17 //驼类
18 class tuo :virtual public animal
19 {
20 
21 };
22 //羊驼类
23 class sheeptuo : public sheep, public tuo
24 {
25 
26 };
27 void test01()
28 {
29     sheeptuo st;
30     st.sheep::m_age = 28;
31     st.tuo::m_age = 18;
32     //当出现菱形继承,两个父类拥有相同数据,需要加作用域区分
33     cout << "st.sheep::m_age  = " << st.sheep::m_age << endl;
34     cout << "st.tuo::m_age  = " << st.tuo::m_age << endl;
35     //这份数据我们知道只需要一份即可,但菱形继承导致数据有两份,资源浪费
36     //虚继承之后共同的数据只有一个所以可以不加作用域
37     cout << "stm_age  = " << st.m_age << endl;
38 }
39 int main()
40 {
41     test01();
42 }

 

 

 

 

 

总结:

  • 菱形继承带来的主要问题是子类继承两份相同的数据,导致资源浪费以及毫无意义
  • 利用虚继承可以解决菱形继承问题
posted on 2022-09-05 19:59  在野武将  阅读(49)  评论(0)    收藏  举报