C++的继承和多态
继承
基本语法
继承是面向对象的三大特征之一
有些类与类之间存在特殊的关系,例如界门纲目科属种
他们都是下级有上级的共性,还有自己的特性
而通过继承可以减少重复代码
基本语法
| 1
 | class 子类(派生类) : 继承方式 父类(基类)    
 | 
子类的成员中包含两大部分
一类是从父类继承过来的,一类是自己增加的成员
从基类继承过来的表现其共性,而新增的成员体现了其个性
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 
 | #include <iostream>
 
 class BasePage
 {
 public:
 void head()
 {
 cot<<1<<2<<3<<endl;
 }
 void foot()
 {
 cout<<1<<2<<3<<endl;
 }
 void left()
 {
 cout<<etc<<endl;
 }
 };
 
 class C# : public BasePage
 {
 public:
 void contnt()
 {
 cout<<C#学习<<endl;
 }
 }
 
 class Python : public BasePage
 {
 public:
 void contnt()
 {
 cout<<Python学习<<endl;
 }
 }
 void test01()
 {
 cout<<"C#页面"<<endl;
 C# c1;
 c1.head();
 c1.foot();
 c1.left();
 c1.content();
 cout<<"Python页面"<<endl;
 Python py;
 py.head();
 py.foot();
 py.left();
 py.content();
 }
 using namespace std;
 int main()
 {
 
 return 0;
 }
 
 | 
继承方式
继承方式一共有三种
- 公共继承:父类中的公共权限在子类中依然是公共权限,保护权限依然是保护权限,但父类的私有权限不可访问。
- 保护继承:父类中的公共权限在子类中改变为保护权限,保护权限依然是保护权限,但父类的私有权限不可访问。
- 私有继承:父类中的公共权限在子类中改变为私有权限,保护权限改变为私有权限,但父类的私有权限不可访问。
继承中的对象模型
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 
 | #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);
 }
 int mian()
 {
 
 return 0;
 }
 
 | 
小技巧
- 利用开发人员命令提示工具查看查看对象模型
- 跳转盘符D;
- 跳转文件路径cd 具体路径
- 查看文件命名
- cd /dl reportSingleClassLayout类名 文件名
继承中的构造和析构的顺序
子类继承父类后,当创建子类对象时,也会调用父类对象
Question:父类和子类的构造和析构顺序是谁先谁后呢?
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 
 | #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()
 {
 Son s;
 }
 int mian()
 {
 test01();
 return 0;
 }
 
 | 
结论
继承中构造和析构的顺序如下
先构造父类再构造子类,先析构子类再析构父亲
继承同名成员处理方式
- 访问子类同名成员 直接访问(也就是说程序默认访问的是子类的同名成员)
- 访问父类同名成员 加作用域
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 
 | #include <iostream>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;
 }
 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.m_a<<endl;
 cout<<"m_a="<<s.Base::m_a<<endl;
 }
 
 void test02()
 {
 Son s;
 s.func();
 s,Base::func();
 s.Base::func(100);
 }
 int mian()
 {
 test01();
 test02();
 return 0;
 }
 
 | 
值得注意的是
- 如果子类中出现和父类同名的成员函数,子类的同名函数会隐藏掉父类中所有的同名函数(有参,无参)
- 而如果想要访问到父类中被隐藏的同名成员函数,需要加作用域 
继承同名静态成员的处理方式
静态成员和非静态成员出现同名,处理方式一致
- 访问子类同名成员 直接访问即可
- 访问父类同名成员 需要加作用域
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 
 | #include <iostream>using namespace std;
 class Base
 {
 public:
 static int m_a;
 static void func()
 {
 cout<<"Base=func"<<endl;
 }
 void func(int a)
 {
 cout<<"Base=func(int a)"<<endl;
 }
 int m_a;
 };
 int Base::m_a= 100;
 class Son :public Base
 {
 public:
 static int m_a;
 static void func()
 {
 cout<<"Base=func"<<endl;
 }
 void func(int a)
 }
 int Son::m_a= 200;
 
 void test01()
 {
 
 Son s;
 cout<<"m_a="<<s.m_a<<endl;
 cout<<"m_a="<<s.Base::m_a<<endl;
 
 cout<<"m_a="<<Son::m_a<<endl;
 cout<<"m_a="<<Base::m_a<<endl;
 
 
 cout<<"m_a="<<Son::Base::m_a<<endl;
 }
 
 void test02()
 {
 
 Son s;
 s.func();
 s,Base::func();
 
 Son::func();
 Son::Base::func();
 }
 int mian()
 {
 test01();
 test02();
 return 0;
 }
 
 | 
特点与上节一致,会进行隐藏父类的同名静态成员函数。
同名静态成员和同名非静态成员的处理方式一样,只不过有两种访问的方式(通过对象和通过类名)
多继承和语法
基本语法
class 子类 :继承方式 父类1,继承方式父类2....
多继承可能会引发父类中有同名成员出现,需要加作用域区分
C++实际开发中不建议使用多继承
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 
 | #include <iostream>using namespace std;
 class Base1
 {
 public:
 Base1()
 {
 m_a  =100;
 }
 int m_a;
 };
 class Base2
 {
 public:
 Base2()
 {
 m_b  =100;
 }
 int m_b;
 };
 class Son :public Base1, public Base2
 {
 public:
 Son()
 {
 m_c=100;
 m_d=100;
 }
 }
 
 void test01()
 {
 Son s;
 cout<<"sizeof Son = "<<sizeof(s)<<endl;
 cout<<"Base1::m_a= "<<s.Base1::m_a<<endl;
 cout<<"Base2::m_a= "<<s.Base2::m_a<<endl;
 }
 
 void test02()
 {
 
 Son s;
 s.func();
 s,Base::func();
 
 Son::func();
 Son::Base::func();
 }
 int mian()
 {
 test01();
 test02();
 return 0;
 }
 
 | 
菱形继承
概念:
两个子类继承与同一个父类,
又有某个类同时继承着两个子类
这种继承被称为菱形继承,或者钻石继承
但是这种继承通常会产生二义性的问题
因此为了解决
我们通常会利用虚继承来解决上述问题
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 
 | #include <iostream>using namespace std;
 class Animal
 {
 pubilc:
 int m_age;
 }
 
 class Sheep : virtual public Animal
 {
 };
 class Tuo virtual public Animal
 {
 };
 
 class SheepTuo:public Sheep,public Tuo;
 void test01()
 {
 SheepTuo st;
 st.Sheep::m_age=18;
 st.TUo::m_age =23;
 cout<<st.Sheep::m_age<<endl;
 cout<<st.Tuo::m_age<<endl;
 cout<<st.m_age<<endl;
 }
 int main()
 {
 return 0;
 }
 
 | 
多态
基本概念
多态分为两类
- 静态多态:函数重载 和 运算符重载 属于静态多态,服用函数名
- 动态多态:子类和虚函数实现运行时多态
静态多态和动态多态的区别:
- 静态多态的函数地址早绑定 - 编译阶段
- 动态多态的函数地址晚绑定 - 运行阶段
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 
 | #include <iostream>using namespace std;
 class Animal
 {
 pubilc:
 void speak()
 {
 cout<<"动物在说话"<<endl;
 }
 }
 class Sheep : public Animal
 {
 public:
 cout<<"羊在叫"<<endl;
 };
 
 
 
 void doSpeak(Animal &animal)
 {
 animal.spaek();
 }
 void test01()
 {
 Sheep sheep;
 dospeak(sheep);
 }
 int main()
 {
 test01;
 return 0;
 }
 
 | 
结果是
动物在说话,原因是地址早绑定,在编译阶段就确定了函数地址。
解决方法: 加上虚函数
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 
 | #include <iostream>using namespace std;
 class Animal
 {
 pubilc:
 
 virtual void speak()
 {
 cout<<"动物在说话"<<endl;
 }
 }
 class Sheep : public Animal
 {
 public:
 cout<<"羊在叫"<<endl;
 };
 
 
 
 void doSpeak(Animal &animal)
 {
 animal.spaek();
 }
 void test01()
 {
 Sheep sheep;
 dospeak(sheep);
 }
 int main()
 {
 test01;
 return 0;
 }
 
 | 
答案就变成了 羊在叫。
满足条件:
1.有继承关系
2.子类重写父类的虚函数
使用方法:
通过对父类的引用 来执行子类对象
重写:
函数的返回值类型 函数名 参数列表 完全一致称为重写