C++核心编程学习-- 类和对象--继承
继承
#include using namespace std;//Java页面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 test01(){ //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() { test01(); return 0;}
继承实现
代码复用
语法 class 子类 :继承方式 父类
子类也成为派生类 父类也成为基类
#include 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 BasePage 公有的内容{public: void content() // 自己的内容 { cout << \"JAVA学科视频\" << endl; }};//Python页面class Python : public BasePage{public: void content() { cout << \"Python学科视频\" << endl; }};//C++页面class CPP : public BasePage{public: void content() { cout << \"C++学科视频\" << endl; }};void test01(){ //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() { test01(); return 0;}
继承方式
公有继承
保护继承
私有继承
公有继承,父类中的访问权限不发生改变,但是子类无法访问父类私有的属性,但是依旧继承了父类的私有属性。
保护继承,父类中除了私有属性子类无法访问外,其余属性都变为保护属性被子类继承。
私有继承,父类中除了私有属性子类无法访问外,其余属性都变为子类的私有属性继承。
class Base1{public: int m_A;protected: int m_B;private: int m_C;};//公共继承class Son1 :public Base1{public: void func() { m_A; //可访问 public权限 m_B; //可访问 protected权限 //m_C; //不可访问 }};void myClass(){ Son1 s1; s1.m_A; //其他类只能访问到公共权限}//保护继承class Base2{public: int m_A;protected: int m_B;private: int m_C;};class Son2:protected Base2{public: void func() { m_A; //可访问 protected权限 m_B; //可访问 protected权限 //m_C; //不可访问 }};void myClass2(){ Son2 s; //s.m_A; //不可访问}//私有继承class Base3{public: int m_A;protected: int m_B;private: int m_C;};class Son3:private Base3{public: void func() { m_A; //可访问 private权限 m_B; //可访问 private权限 //m_C; //不可访问 }};class GrandSon3 :public Son3{public: void func() { //Son3是私有继承,所以继承Son3的属性在GrandSon3中都无法访问到 //m_A; //m_B; //m_C; }};
继承中的对象模型
从父类继承过来的属性,那些属于对象模型。
父类中所有的非静态成员属性都会被子类继承下去。
父类中私有属性 被编译器隐藏了,因此访问不到,但是继承到了。
#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 test01(){ cout << \"sizeof Son = \" << sizeof(Son) << endl; // 16 }int main() { test01(); system(\"pause\"); return 0;}
继承中构造和析构的顺序
子类继承父类后,当创建子类对象,也会调用父类的构造函数
父类和子类构造的顺序
先构造父类,再构造子类,析构的顺序与构造的顺序相反
#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 test01(){ // 继承中先调用父类的构造函数, 再调用子类的构造函数 Son s;}int main(){ test01(); return 0;}
继承同名成员处理方式
当子类与父类出现同名的成员,如何通过子类对象,访问到子类或者父类中同名的数据?
访问子类同名成员 直接访问即可
访问父类同名成员 需要加作用域
同名成员属性处理方式
如果重名了,创建一个子类对象,直接访问就是子类的同名属性。
因为子类继承了父类的属性,所以子类也可以通过作用域s.Base::m_A进行访问父类的同名属性。
如果创建一个父类对象,也可以直接访问父类中的属性parent.m_A;
同名成员函数处理方式
与成员属性的规则一样
但是如果父类中出现函数重载,
继承的子类中如果有父类的同名函数,子类会隐藏掉所有的父类同名函数。
子类需要访问父类中的同名重载函数,依旧需要加作用域。
#include using namespace std;class Base{public: int m_A; Base() { m_A = 100; } void func() { cout << \"Base::func()\" << endl; } void func(int a) { cout << \"Base::func(int a)\" << endl; }};class Son : public Base{public: int m_A; Son() { m_A = 200; } // 如果子类与父类拥有同名的成员函数,子类会隐藏掉父类中所有同名成员函数, // 如果想访问父类中被隐藏的同名成员函数,需要加父类的作用域 void func() { cout << \"Son::func()\" << endl; }};void test01(){ 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(100);}int main(){ test01(); return 0;}
继承中同名的静态成员处理方式
静态成员与上面的成员处理方式一样
#include using namespace std;class Base{public: static int m_A; static void func() { cout << \"Base::func()\" << endl; } static void func(int a) { cout << \"Base::func(int a)\" << endl; }};int Base::m_A = 100;class Son : public Base{public: static int m_A; static void func() { cout << \"Son::func()\" << endl; } static void func(int a) { cout << \"Son::func(int a)\" << endl; }};int Son::m_A = 200;// 同名成员属性void test01(){ // 通过对象访问 静态成员有两种访问方式 一种是通过对象 一种是通过类名 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 = \" << Base::m_A << endl; // 通过Son这个类名进行访问静态属性,访问Base作用域下的m_A属性 cout << \"Base 下 m_A = \" << Son::Base::m_A << endl;}// 同名成员函数void test02(){ // 通过对象访问 cout << \"通过对象访问:\" << endl; Son s; s.func(); s.Base::func(); // 通过类名访问 cout << \"通过类名访问:\" << endl; Son::func(); Son::Base::func(); // 出现同名,子类会隐藏父类中所有同名成员函数,需要加作用域访问 Son::Base::func(100);}int main(){ test01(); //test02(); system(\"pause\"); return 0;}
多继承语法
C++允许一个类继承多个类
也就是允许一个儿子继承多个爹
实际开发中不建议用多继承
出现多个同名,需要加某个父类的作用域
#include using namespace std;class Base1{public: int m_A; Base1() { m_A = 100; }};class Base2{public: int m_A; Base2() { m_A = 200; }};// 语法: class 子类 : 继承方式 Base1, 继承方式 Base2class Son: public Base1, public Base2{public: int m_C; int m_D; Son() { m_C = 300; m_D = 400; }};// 多继承容易产生成员同名的情况,通过使用类名作用域可以区分调用哪一个类的成员void test01(){ Son s; cout << \"sizeof Son\" << sizeof(Son) << endl; // cout << s.Base1::m_A << endl; cout << s.Base2::m_A << endl;}int main(){ test01();}
菱形继承
钻石继承
两个派生类继承同一个基类,又有某个类同时继承者两个派生类,这种继承被称为菱形继承,或者钻石继承。
下面的继承结构: 羊继承动物,骆驼继承动物,草泥马继承羊和骆驼
动物父类中动物年龄的属性分别被羊和骆驼继承
但是草泥马在继承中就产生了二义性,羊和骆驼都有一个年龄属性。
可以通过作用域解决同名问题,但是无法解决继承了多份数据的问题
虚继承
利用virtual 虚继承能够解决菱形继承中的二义性问题
普通继承
class Sheep :public Animal{}
虚继承
class Sheep :virtual public Animal{}
其中 Animal 被称为 虚基类
虚继承确保了所有路径访问的是同一物理内存,但这是通过编译器生成的隐藏机制(如虚基类表)实现的
本质是虚基类指针指向同一份、唯一的数据。
加上virtual关键字的虚基类,会生成vbptr虚基类指针,指向虚基类表vbtable,虚基类表中存放了所有虚继承派生类相对于基类的偏移量,加上这个偏移量就能找到基类中唯一的数据。
解决菱形继承问题,virtual需要放在共同继承一个父类的子类上,然后后面共同继承这两个的子类的对象访问同名属性时,才会找到父类中唯一的属性。
在 C++ 中,虚继承(virtual inheritance)的 virtual 关键字必须放在直接继承虚基类的类(即 B 和 C)上,而不能放在最终派生类(如 D)上。
这里与虚函数区分,运行时多态的虚函数,是将关键字加在父类的函数中,后续的子类去重写父类的虚函数,这样实现多态。晚绑定。
#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 test01(){ SheepTuo st; st.Sheep::m_Age = 100; st.Tuo::m_Age = 200; cout << \"Sheep age = \" << st.Sheep::m_Age << endl; cout << \"Tuo age = \" << st.Tuo::m_Age << endl; cout << \"SheepTuo age = \" << st.m_Age << endl; // 相同的属性数据只有一份了}int main(){ test01(); return 0;}