• 浅谈C++|类的继承篇


     

    引子: 

    继承是面向对象三大特性之一、有些类与类之间存在特殊的关系,例如下图中:



    我们发现,定义这些类时,下级别的成员除了拥有上一级的共性,还有自己的特性。
    这个时候我们就可以考虑利用继承的技术,减少重复代码。

    一.继承基本语法 

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

    优点:减少重复代码

    子类也叫派生类,父类也叫基类

    代码: 

    1. #include
    2. using namespace std;
    3. class person { //定义person基类
    4. public:
    5. int age;
    6. int height;
    7. int weight;
    8. string name;
    9. void show() {
    10. cout << "age=" << age << endl;
    11. cout << "height=" << height << endl;
    12. cout << "weight=" << weight << endl;
    13. cout << "name=" << name << endl;
    14. }
    15. };
    16. class women :public person {
    17. public:
    18. string xingbie;
    19. void shou_x() {
    20. cout << "xingbie=" << xingbie<
    21. }
    22. };
    23. void fun() {
    24. women p;
    25. p.age = 18;
    26. p.height = 180;
    27. p.name = "tom";
    28. p.weight = 160;
    29. p.xingbie = "女";
    30. p.show();
    31. p.shou_x();
    32. }
    33. int main() {
    34. fun();
    35. return 0;
    36. }

     派生类中的成员,包含两大部分:
    1.类是从基类继承过来的,-类是自己增加的成员。
    2.从基类继承过过来的表现其共性,而新增的成员体现了其个性。

     二.继承方式

     继承方式:

    public:     公有继承

    protected:保护继承

    private:    私有继承

    代码:

    1. #include
    2. using namespace std;
    3. class father {
    4. public:
    5. int A;
    6. protected:
    7. int B;
    8. private:
    9. int C;
    10. };
    11. class son1 :public father { //公有继承
    12. public:
    13. void fun() {
    14. cout << "A=" << A << endl; //公有变为公有
    15. cout << "B=" << B << endl; //保护变为保护
    16. //cout << "C=" << C << endl;//私有不可访问
    17. }
    18. };
    19. class son2 :protected father { //保护继承
    20. public:
    21. void fun() {
    22. cout << "A=" << A << endl; //公有变为保护
    23. cout << "B=" << B << endl; //保护变为保护
    24. //cout << "C=" << C << endl;//私有不可访问
    25. }
    26. };
    27. class son3 :private father { //私有继承
    28. public:
    29. void fun() {
    30. cout << "A=" << A << endl; //公有变为私有
    31. cout << "B=" << B << endl; //保护变为私有
    32. //cout << "C=" << C << endl;//私有不可访问
    33. }
    34. };
    35. void fun() {
    36. son1 p1;
    37. son2 p2;
    38. son3 p3;
    39. p1.A = 10;
    40. p1.fun();
    41. }
    42. int main() {
    43. fun();
    44. return 0;
    45. }

     私有成员全不见,公不变,保全保,私全私。

     三.继承中的对象模型

    继承中,基类私有变量虽然访问不到,但是已经被继承,只是被隐藏了。

     代码:

    1. #include
    2. using namespace std;
    3. class father {
    4. public:
    5. int A;
    6. protected:
    7. int B;
    8. private:
    9. int C;
    10. };
    11. class son :private father {
    12. public:
    13. int age;
    14. };
    15. int main() {
    16. cout << sizeof(son) << endl;
    17. return 0;
    18. }

    四.继承构造和析构顺序

    子类继承父类时,会先创建一个父类。析构的顺序和构造顺序正好相反。

     代码:

    1. #include
    2. using namespace std;
    3. class father {
    4. public:
    5. father() {
    6. cout << "father的构造哈数" << endl;
    7. }
    8. ~father() {
    9. cout << "father的析构函数" << endl;
    10. }
    11. };
    12. class son :public father {
    13. public:
    14. son() {
    15. cout << "son的构造函数" << endl;
    16. }
    17. ~son() {
    18. cout << "son的析构函数" << endl;
    19. }
    20. };
    21. void fun() {
    22. son a;
    23. }
    24. int main() {
    25. fun();
    26. return 0;
    27. }

    五.继承同名成员处理方式

    当基类中的成员变量以及成员函数,和派生类的成员变量和函数重名时,基类默认调用派生类的成员函数和成员变量。要想调用基类的成员函数和成员变量,需要加上基类的作用域;

    代码:

    1. #include
    2. using namespace std;
    3. class father {
    4. public:
    5. int A;
    6. father() {
    7. A = 999;
    8. }
    9. void fun() {
    10. cout << "father的fun调用" << endl;
    11. }
    12. void fun(int a) {
    13. cout << "father的fun调用" << ' ' << a << endl;
    14. }
    15. };
    16. class son :public father{
    17. public:
    18. int A;
    19. son() {
    20. A = 99;
    21. }
    22. void fun() {
    23. cout << "son的fun调用" << endl;
    24. }
    25. void fun(int a) {
    26. cout << "father的fun调用" << ' ' << a << endl;
    27. }
    28. };
    29. void fun() {
    30. son p;
    31. cout << "son" << ' ' << p.A << endl;
    32. cout << "father" << ' ' << p.father::A << endl;
    33. p.fun();
    34. p.fun(11);
    35. p.father::fun();
    36. p.father::fun(90);
    37. }
    38. int main() {
    39. fun();
    40. return 0;
    41. }

     如果派生类中出现和基类重名的成员函数,那么派生类会隐藏基类全部重名的成员函数。需要注意的是,本来可以按照函数重载区分,却被派生类隐藏的情况:

    代码: 

    1. #include
    2. using namespace std;
    3. class father {
    4. public:
    5. int A;
    6. father() {
    7. A = 999;
    8. }
    9. void fun() {
    10. cout << "father的fun调用" << endl;
    11. }
    12. void fun(int a,int b) {
    13. cout << "father的fun调用" << ' ' << a << endl;
    14. }
    15. };
    16. class son :public father{
    17. public:
    18. int A;
    19. son() {
    20. A = 99;
    21. }
    22. void fun() {
    23. cout << "son的fun调用" << endl;
    24. }
    25. };
    26. void fun() {
    27. son p;
    28. cout << "son" << ' ' << p.A << endl;
    29. cout << "father" << ' ' << p.father::A << endl;
    30. p.fun();
    31. p.fun();
    32. p.father::fun();
    33. //p.fun(90,89);//报错
    34. p.father::fun(89, 123);
    35. }
    36. int main() {
    37. fun();
    38. return 0;
    39. }

    六.继承同名static成员处理方式

     当重名的成员变量和函数是static时,需要注意通过类名来调用的方式

    代码:

    1. #include
    2. using namespace std;
    3. class father {
    4. public:
    5. static int A;
    6. static void fun() {
    7. cout << "father的fun调用" << endl;
    8. }
    9. void fun(int a) {
    10. cout << "father的fun调用" << a<<' ' << endl;
    11. }
    12. };
    13. int father::A=10; //static成员变量,类内声明,类外定义
    14. class son :public father {
    15. public:
    16. static int A;
    17. static void fun() {
    18. cout << "son的fun调用" << endl;
    19. }
    20. };
    21. int son::A = 20;
    22. void fun() {
    23. son p;
    24. //1.利用对象调用成员变量和函数
    25. cout << p.A << endl;
    26. cout << p.father::A << endl;
    27. p.fun();
    28. p.father::fun();
    29. cout << "**************************" << endl;
    30. //2.利用类名调用成员变量和函数
    31. cout << son::A << endl;
    32. cout << father::A << endl;
    33. cout << son::father::A << endl;
    34. son::fun();
    35. father::fun();
    36. son::father::fun();
    37. //father::fun(10); //报错,此方法只能调用static类型
    38. }
    39. int main() {
    40. fun();
    41. return 0;
    42. }

    七.多继承语法

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


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


    多继承可能会引发父类中有同名成员出现,需要加作用域区分
    C++实际开发中不建议用多继承

     代码:

    1. #include
    2. using namespace std;
    3. class base1 {
    4. public:
    5. int A;
    6. base1() {
    7. A = 10;
    8. cout << "base1构造函数调用" << endl;
    9. }
    10. };
    11. class base2 {
    12. public:
    13. int A;
    14. base2() {
    15. A = 20;
    16. cout << "base2构造函数调用" << endl;
    17. }
    18. };
    19. class son :public base1, public base2 {
    20. public:
    21. int B;
    22. int C;
    23. son() {
    24. B = 100;
    25. C = 200;
    26. cout << "son构造函数调用" << endl;
    27. }
    28. };
    29. void fun() {
    30. son b;
    31. cout << b.base1::A << endl;
    32. cout << b.base2::A << endl;
    33. cout << b.B << endl;
    34. cout << b.C << endl;
    35. }
    36. int main() {
    37. fun();
    38. return 0;
    39. }

     

     注意构造函数的调用顺序,父亲1先调用,父亲2再调用,最后son调用

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

    八.菱形继承

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

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

     代码:

    1. #include
    2. using namespace std;
    3. class dongwu {
    4. public:
    5. int A;
    6. };
    7. class yang :public dongwu {};
    8. class tuo:public dongwu {};
    9. class son :public tuo, public yang {};
    10. void fun() {
    11. son p;
    12. p.yang::A = 100;
    13. p.tuo::A = 200;
    14. //当菱形继承,两个父亲拥有相同名的成员时,要加作用域加以区分;
    15. cout << p.yang::A << endl;
    16. cout << p.tuo::A << endl;
    17. }
    18. int main() {
    19. fun();
    20. return 0;
    21. }

    此时的继承情况是,这样的。 

     利用虚继承,可以解决菱形继承的问题(两份相同的数据,浪费内存)

    代码:

    1. #include
    2. using namespace std;
    3. class dongwu {
    4. public:
    5. int A;
    6. };
    7. //virtual虚继承,dongwu称为虚基类
    8. class yang :virtual public dongwu {};
    9. class tuo:virtual public dongwu {};
    10. class son :public tuo, public yang {};
    11. void fun() {
    12. son p;
    13. yang p1;
    14. tuo p2;
    15. p.yang::A = 100;
    16. p.tuo::A = 200;
    17. p1.A = 90;
    18. p2.A = 190;
    19. //当菱形继承,两个父亲拥有相同名的成员时,要加作用域加以区分;
    20. cout << p.yang::A << endl;
    21. cout << p.tuo::A << endl;
    22. cout << p1.A << endl;
    23. cout << p2.A << endl;
    24. }
    25. int main() {
    26. fun();
    27. return 0;
    28. }

     继承时只继承一份,此时son继承的是两个虚基类表的指针,虚基类表中存储的是到成员的偏移量。

    总结:
    ·菱形继承带来的主要问题是子类继承两份相同的数据,导致资源浪费以及毫无意义·利用虚继承可以解决菱形继承问题

  • 相关阅读:
    Sonatype Nexus 如何把多仓库合并在一起
    yocto machine class解析之st-partitions-image
    Java面试八股文整理
    机器学习吴恩达课程学习笔记 Part1——从机器学习概念到线性回归
    【云原生之K8s】 list-watch机制及调度约束
    西北工业大学算法实验机试复习
    【数据结构】堆,堆的实现,堆排序,TOP-K问题
    neo4j数据库导出
    iOS 开发中上传 IPA 文件的方法(无需 Mac 电脑)
    高手过招不用鼠标,一款超好用的跨平台命令行界面库
  • 原文地址:https://blog.csdn.net/m0_73731708/article/details/132927486