当前位置:   article > 正文

C++中的多态(虚函数、协变、final、override)函数的重载、重写(覆盖)、隐藏(重定义)的区别_协变是重写吗

协变是重写吗

目录

 

多态的概念:不同的对象完成同一行为时,展现出不同的形态。

1、多态的定义及实现

1.1、定义

1.1.1、虚函数的定义

1.1.2、虚函数的重写

1.2、虚函数重写的两个例外

1.2.1、协变

1.2.2、析构函数的重写

2.3、C++11中的final和override

2.3.1、final:修饰虚函数表示该函数不再被继承

2.3.2、override:检查派生类的虚函数是否重写了基类的某个虚函数,如果没有重写编译会报错。

2.4、函数的重载、重写(覆盖)、隐藏(重定义)的区别


多态的概念:不同的对象完成同一行为时,展现出不同的形态。

比如买票这件事情,当成人买票是全价,学生买票是半价,儿童不需要买票。不同的对象完成同一件事产生不同的结果。

1、多态的定义及实现

1.1、定义

拥有不同继承关系的对象,在调用相同的类成员时产生出不同的结果。

多态构成的条件:

  • 派生类和基类当中必须有两个函数名,参数列表、返回值相同的虚函数,构成重写关系(派生类中同名函数不加virtual关键字时,由于继承关系系统默认它为虚函数。)
  • 必须通过基类的指针或者引用调用虚函数(因为只用当派生类对象通过指针或者引用转换成基类对象指针时,通过基类指针对象才能将重写后的虚函数调用出来,不这样做基类会调用基类成员,派生类也一样。)
  • 被调用的函数必须是虚函数,并且派生类必须对基类的虚函数进行重写。

1.1.1、虚函数的定义

        在类中被virtual关键字修饰的函数,叫做虚函数。

1.1.2、虚函数的重写

        只有虚函数才会重写,并且满足的条件是:两个虚函数函数名相同、参数列表相同、返回值类型相同、就会构成重写(或者叫覆盖),函数重写之后,基类的虚函数所实现的功能将会被派生类同名虚函数的功能所覆盖。

  1. #include<iosteam>
  2. using namespace std;
  3. class Person {
  4. public:
  5. virtual void work() { cout << "上课" << endl; }
  6. };
  7. class Student : public Person {
  8. public:
  9. virtual void work() { cout << "听课" << endl; }
  10. //此处函数如果没有virtual修饰也构成重写关系
  11. };
  12. void Func(Person& p)
  13. { p.work(); }
  14. int main()
  15. {
  16. Person ps;
  17. Student st;
  18. Func(ps);
  19. Func(st);
  20. return 0;
  21. }

1.2、虚函数重写的两个例外

1.2.1、协变

派生类重写基类时,两个虚函数的返回值类型不同,基类返回的是基类对象的指针或者引用,派生类返回派生类对象的指针或者引用。通过下面这个例子大家就能理解。

  1. #include <iostream>
  2. using namespace std;
  3. /* 什么叫做协变呢,一共有四个类,A B Person Student 类,B继承自A,Student继承自
  4. Person 当Person中虚函数返回值为A的指针或者引用,Student中的同名虚函数返回B类的
  5. 对象指针或者引用的时候两个类构成重写*/
  6. class A
  7. {};
  8. class B : public A
  9. {};
  10. //class Person
  11. //{
  12. //public:
  13. // virtual A* f()
  14. // { return new A; }
  15. //};
  16. //class Student : public Person
  17. //{
  18. //public:
  19. // virtual B* f()
  20. // {
  21. //
  22. // return new B;
  23. // }
  24. //};
  25. class Person
  26. {
  27. public:
  28. virtual Person* f()
  29. {
  30. return new Person;
  31. }
  32. };
  33. class Student : public Person
  34. {
  35. public:
  36. virtual Student* f()
  37. {
  38. return new Student;
  39. }
  40. };
  41. int main()
  42. {
  43. A a;
  44. B b;
  45. Person p;
  46. Student s;
  47. p.f();
  48. s.f();
  49. system("pause");
  50. return 0;
  51. }

1.2.2、析构函数的重写

为什么会出现析构函数的重写呢?明明他们俩的函数名并不相同.

此处就要说了,起始所有类的析构函数底层都是通过destructor实现的,在底层他们的名字是相同的。

那它的应用场景是什么呢?

一般情况下我们是不会在类中实现析构函数的,使用的是系统提供的默认析构函数,但是一旦当我们实现之后,系统将会使用用户定义的析构。当我们将派生类指针强转成基类指针时,并且派生类重写了基类虚析构函数,在如下情境下,系统销毁对象时,会先调用派生类的析构函数,再会调用基类的构造函数,保证了正常的析构过程

  1. #include <iostream>
  2. using namespace std;
  3. class Person {
  4. public:
  5. virtual ~Person() { cout << "~Person()" << endl; }
  6. };
  7. class Student : public Person {
  8. public:
  9. virtual ~Student() { cout << "~Student()" << endl; }
  10. };
  11. // 只有派生类Student的析构函数重写了Person的析构函数,下面的delete对象调用析构函数,才能构成
  12. //多态,才能保证p1和p2指向的对象正确的调用析构函数。
  13. int main()
  14. {
  15. Person* p1 = new Person;
  16. Person* p2 = new Student;
  17. delete p1;
  18. delete p2;
  19. //system("pause");
  20. return 0;
  21. }

2.3、C++11中的final和override

2.3.1、final:修饰虚函数表示该函数不再被继承

2.3.2、override:检查派生类的虚函数是否重写了基类的某个虚函数,如果没有重写编译会报错。

2.4、函数的重载、重写(覆盖)、隐藏(重定义)的区别

重载:

  • 两个函数在相同的作用域
  • 函数名和参数列表相同

重写:

  • 两个函数分别在派生类和基类作用域
  • 函数名、参数列表、返回值相同(协变、虚析构函数重载)
  • 两个函数是虚函数

隐藏:

  • 两个函数分别在派生类和基类作用域
  • 函数名相同

 

 

 

 

 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/酷酷是懒虫/article/detail/950570
推荐阅读
  

闽ICP备14008679号