当前位置:   article > 正文

C++的动态多态,Virtual关键字的作用

virtual关键字的作用

 1.虚函数

        virtual用来修饰父类函数中的普通函数,子类可以通过重写(与父类中的普通函数同名同参数)来覆盖父类中的函数的输出结果,即程序在运行时调用的是子类中的函数而不是父类。注意:在调用函数时(Barking()),我们用的父类的指针或引用指向子类对象。最终输出的结果是:

  1. #include <iostream>
  2. #include <string>
  3. using namespace std;
  4. class Animals
  5. {
  6. public:
  7. virtual void Bark()
  8. {
  9. cout << "动物叫声" << endl;
  10. }
  11. };
  12. class Cat :public Animals
  13. {
  14. public:
  15. virtual void Bark()
  16. {
  17. cout << "喵喵喵" << endl;
  18. }
  19. };
  20. void Barking(Animals& animals)
  21. {
  22. animals.Bark();
  23. }
  24. void text()
  25. {
  26. Cat cat;
  27. Barking(cat);
  28. }
  29. int main()
  30. {
  31. text();
  32. }

2.纯虚函数

        在解释纯虚函数之前,需要先明白一个问题,我们为什么要定义父类指针去指向子类对象,而不是直接用子类指针指向子类对象,下面这篇博客很好的解释了这个问题。父类引用指向子类对象_gideal_wang的博客-CSDN博客父类引用指向子类对象指的是:例如父类Animal,子类Cat,Dog。其中Animal可以是类也可以是接口,Cat和Dog是继承或实现Animal的子类。Animal animal = new Cat();即声明的是父类,实际指向的是子类的一个对象。 那这么使用的优点是什么,为什么要这么用?可以用这几个关键词来概括:多态、动态链接,向上转型也有人说这是面向接口编程,可以降低https://blog.csdn.net/gideal_wang/article/details/4913965?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522166692105116782427487200%2522%252C%2522scm%2522%253A%252220140713.130212432.pc%255Fall.%2522%257D&request_id=166692105116782427487200&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~first_rank_ecpm_v1~pc_ctr_v1-8-4913965-null-null.142%5Ev62%5Eopensearch_v2,201%5Ev3%5Eadd_ask,213%5Ev1%5Econtrol&utm_term=%E7%88%B6%E7%B1%BB%E6%8C%87%E9%92%88%E6%8C%87%E5%90%91%E5%AD%90%E7%B1%BB%E5%AF%B9%E8%B1%A1&spm=1018.2226.3001.4187        虚函数的输出结果可知,若所有子类中均重写了父类的一个虚函数,那么程序将不会调用父函数中的虚函数,这时,我们可以将这个父类中的虚函数定义成一个纯虚函数。

        纯虚函数的定义方式:virtual  返回值类型  函数名(参数列表)  =  0;

        值得注意的是,当类中出现了纯虚函数,那么该类不能实例化对象;子类继承了父类中的纯虚函数而不是重写父类中的纯虚函数时,子类也不能实例化对象。

  1. #include <iostream>
  2. #include <string>
  3. using namespace std;
  4. class Animals
  5. {
  6. public:
  7. virtual void Bark() = 0;
  8. };
  9. class Cat :public Animals
  10. {
  11. public:
  12. virtual void Bark()
  13. {
  14. cout << "喵喵喵" << endl;
  15. }
  16. };
  17. void Barking(Animals& animals)
  18. {
  19. animals.Bark();
  20. }
  21. void text()
  22. {
  23. Cat cat;
  24. Barking(cat);
  25. }
  26. int main()
  27. {
  28. text();
  29. }

3.虚析构函数

        若不使用virtual修饰父类的析构函数,又使用了父类指针去指向子类对象,我们在最后delete实例化的对象(cat)的时候,系统不会调用Cat类的析构函数。运行结果:

  1. #include <iostream>
  2. #include <string>
  3. using namespace std;
  4. class Animals
  5. {
  6. public:
  7. virtual void Bark() = 0;
  8. Animals()
  9. {
  10. cout << "Animals的构造" << endl;
  11. }
  12. ~Animals()
  13. {
  14. cout << "Animals的析构" << endl;
  15. }
  16. };
  17. class Cat :public Animals
  18. {
  19. public:
  20. Cat()
  21. {
  22. cout << "Cat的构造" << endl;
  23. }
  24. virtual void Bark()
  25. {
  26. cout << "喵喵喵" << endl;
  27. }
  28. ~Cat()
  29. {
  30. cout << "Cat的析构" << endl;
  31. }
  32. };
  33. void text()
  34. {
  35. Animals* cat = new Cat;
  36. cat->Bark();
  37. delete cat;
  38. }
  39. int main()
  40. {
  41. text();
  42. }

        若在使用Cat类的构造函数中new一个类(也就是自己申请一块地址,该参数存放在堆区,需要手动delete),而父类又不能调用子类的析构函数,这时我们需要用到virtual关键字去修饰父类的析构函数,从而达到手动释放堆区参数的目的。最终结果:

  1. #include <iostream>
  2. #include <string>
  3. using namespace std;
  4. class Animals
  5. {
  6. public:
  7. virtual void Bark() = 0;
  8. Animals()
  9. {
  10. cout << "Animals的构造" << endl;
  11. }
  12. virtual ~Animals()
  13. {
  14. cout << "Animals的析构" << endl;
  15. }
  16. };
  17. class Cat :public Animals
  18. {
  19. public:
  20. string* m_Name;
  21. Cat(string name)
  22. {
  23. m_Name = new string(name);
  24. cout << "Cat的构造" << endl;
  25. }
  26. virtual void Bark()
  27. {
  28. cout << *m_Name << "喵喵喵" << endl;
  29. }
  30. ~Cat()
  31. {
  32. if (m_Name != NULL)
  33. {
  34. delete m_Name;
  35. //delete一个指针后,编译器只是释放了指针中存放的地址中的内存空间,
  36. //但p中存放的地址还是原来的地址,
  37. //这时成为一个野指针,为了杜绝这种情况的发生,我们需要置NULL
  38. m_Name = NULL;
  39. }
  40. cout << "Cat的析构" << endl;
  41. }
  42. };
  43. void text()
  44. {
  45. Animals* cat = new Cat("Tom");
  46. cat->Bark();
  47. delete cat;
  48. }
  49. int main()
  50. {
  51. text();
  52. }

        题外:为什么需要new一个类,而不是直接用最基础的参数定义方式,就是int number这种自动变量。其中一个原因是自动变量在离开作用域后回自动释放。

3.纯虚析构函数

        感觉没屁用。。。

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

闽ICP备14008679号