多态典型用例之virtual

参考:https://www.cnblogs.com/dormant/p/5223215.html

1.虚函数(virtual)

(1)在某基类中声明为 virtual 并在一个或多个派生类中被重新定义的成员函数。实现多态性,通过指向派生类的基类指针或引用,访问派生类中同名覆盖成员函数。

实例:

  1. #include "pch.h"
  2. #include <iostream>
  3. using namespace std;
  4. class A
  5. {
  6. public:
  7. void print()
  8. {
  9. cout<<"class A"<<endl;
  10. }
  11. };
  12. class B:public A
  13. {
  14. public:
  15. void print()
  16. {
  17. cout<<"class B"<<endl;
  18. }
  19. };
  20. int main()
  21. {
  22. A a;
  23. B b;
  24. a.print();
  25. b.print();
  26. return 0;
  27. }

输出结果:

(2)通过class A和class B的print()这个接口,可看出两个class采用了不同的策略,但这并不是多态性行为(使用的是不同类型的指针),没有用到虚函数的功能。改main函数为如下:

  1. int main()
  2. {
  3. A a;
  4. B b;
  5. A *P1=&a;
  6. A *p2=&b;
  7. p1->print();
  8. p2->print();
  9. return 0;
  10. }

输出结果:

(3)p2明明指向的是class B的对象但却是调用的class A的print()函数,这不是我们所期望的结果,那么解决这个问题就需要用到虚函数。

  1. class A
  2. {
  3. public:
  4. virtual void print()
  5. {
  6. cout<<"class A"<<endl;
  7. }
  8. };
  9. class B:public A
  10. {
  11. public:
  12. void print()
  13. {
  14. cout<<"class B"<<endl;
  15. }
  16. };

输出结果:

现在,class A的成员函数print()已经成了虚函数 class B的print()也成了虚函数了。我们只需在把基类的成员函数设为virtual,其派生类的相应的函数也会自动变为虚函数。(对于在派生类的相应函数前是否需要用virtual关键字修饰,语法上可加可不加,不加的话编译器会自动加上,但为了阅读方便和规范性,建议加上)

总结:指向基类的指针在操作它的多态类对象时,会根据不同的类对象,调用其相应的函数,这个函数就是虚函数。

2.虚析构函数

当一个类有子类时,该类的析构函数必须是虚函数,原因:会有资源释放不完全的情况。

  1. #include "pch.h"
  2. #include <iostream>
  3. using namespace std;
  4. class Base
  5. {
  6. public:
  7. ~Base()
  8. {
  9. cout << "~Base()" << endl;
  10. }
  11. };
  12. class Derived :public Base
  13. {
  14. public:
  15. Derived()
  16. {
  17. p = new int(0);
  18. }
  19. ~Derived()
  20. {
  21. cout << "~Derived()" << endl;
  22. delete p;
  23. }
  24. private:
  25. int *p;
  26. };
  27. void fun(Base *b)
  28. {
  29. delete b;
  30. }
  31. int main()
  32. {
  33. Base *b = new Derived();
  34. fun(b);
  35. return 0;
  36. }

输出结果:

这里可以看到,对象销毁时只调用了父类的析构函数。如果这时子类的析构函数中有关于内存释放的操作,将会造成内存泄露。所以需要给父类的析构函数加上virtual。

  1. class Base
  2. {
  3. public:
  4. virtual ~Base()
  5. {
  6. cout << "~Base()" << endl;
  7. }
  8. };

输出结果:

3.纯虚函数(抽象函数)

纯虚函数是一种特殊的虚函数,在许多情况下,在基类中不能对虚函数给出有意义的实现,而把它声明为纯虚函数,它的实现留给该基类的派生类去做。这就是纯虚函数的作用。

纯虚函数就是没有函数体,同时在定义的时候,其函数名后面要加上“= 0”。

  1. class base1
  2. {
  3. public:
  4. virtual void display() const=0;
  5. };
  1. int main()
  2. {
  3. base1 b1;//错误
  4. base2 b2;
  5. derived d;
  6. fun(&b1);
  7. fun(&b2);
  8. fun(&d);
  9. return 0;
  10. }

图:

base1是一个抽象类,不能实例化。

例1:

  1. #include "pch.h"
  2. #include <iostream>
  3. using namespace std;
  4. class base1
  5. {
  6. public:
  7. virtual void display() const=0;
  8. };
  9. class base2 :public base1
  10. {
  11. public:
  12. virtual void display() const;
  13. };
  14. void base2::display() const
  15. {
  16. cout << "base2::display()" << endl;
  17. }
  18. class derived :public base2
  19. {
  20. public:
  21. virtual void display() const;
  22. private:
  23. };
  24. void derived::display() const
  25. {
  26. cout << "derived::display()" << endl;
  27. }
  28. void fun(base1 *ptr)
  29. {
  30. ptr->display();
  31. }
  32. int main()
  33. {
  34. base2 base2;
  35. derived derived;
  36. fun(&base2);
  37. fun(&derived);
  38. return 0;
  39. }

输出结果:

例2:

  1. #include "pch.h"
  2. #include<iostream>
  3. using namespace std;
  4. class Fish
  5. {
  6. public:
  7. virtual void water() = 0;
  8. virtual void eat() = 0;
  9. };
  10. class Shark : public Fish
  11. {
  12. public:
  13. void water();
  14. void eat();
  15. };
  16. void Shark::eat(){cout<<"Shark eat. "<<endl;}
  17. void Shark::water(){cout<<"Shark water. "<<endl;}
  18. void fun(Fish *f)
  19. {
  20. f->eat();
  21. f->water();
  22. }
  23. void main()
  24. {
  25. Shark s;
  26. Fish *f = &s;
  27. fun(f);
  28. }

输出结果:

a.定义纯虚函数时,不能定义纯虚函数的实现部分。即使是函数体为空也不可以,函数体为空就可以执行,只是什么也不做就返回。而纯虚函数不能调用。

(其实可以写纯虚函数的实现部分,编译器也可以通过,但是永远也无法调用。因为其为抽象类,不能产生自己的对象,而且子类中一定会重写纯虚函数,因此该类的虚表内函数一定会被替换掉,所以可以说永远也调用不到纯虚函数本身)

b."=0"表明程序将不定义该函数,函数声明是为派生类保留一个位置。“=0”的本质是将指向函数体的指针定为NULL。

c.在派生类中必须有重新定义的纯虚函数的函数体,这样的派生类才能用来定义对象。(如果不重写进行覆盖,程序会报错)

多态典型用例之virtual的更多相关文章

  1. Faas 典型场景——应用负载有显著的波峰波谷,典型用例-基于事件的数据处理

    Serverless适用的两大场景 场景一:应用负载有显著的波峰波谷 Serverless化与否的评判标准并不是公司规模的大小,而是其业务背后的具体技术问题,比如业务波峰波谷明显,如何实现削峰填谷.一 ...

  2. 07——为多态基类声明为virtual析构函数

    当基类确定被继承的时候,析构函数声明为virtual是必须的 当返回的派生类的指针或引用的时候,调用析构函数容易发生内存泄漏 当基类作为抽象类使用,声明pure virtual析构函数 析构函数的顺序 ...

  3. go语言多态接口样例

    感觉比java玄幻啊~~~ package main import ( "fmt" ) type notifier interface{ notify() } type user ...

  4. log4j-slf4j 典型用例

    一.maven 配置 <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j ...

  5. C++:C++的两种多态形式

    // // main.cpp // Test.cpp // // Created by mac on 15/8/11. // Copyright (c) 2015年. All rights reser ...

  6. 转:C++中多态是怎样实现的?

    多态是一种不同的对象以单独的方式作用于相同消息的能力,这个概念是从自然语言中引进的.例如,动词“关闭”应用到不同的事务上其意思是不同的.关门,关闭银行账号或关闭一个程序的窗口都是不同的行为:其实际的意 ...

  7. 4.1 C++多态的概念及前提条件

    参考:http://www.weixueyuan.net/view/6370.html 总结: 而多态的功能则是将函数名动态绑定到函数入口地址,这样的动态绑定过程称为运行期绑定. 而在运行期绑定的函数 ...

  8. C++多态,虚函数,虚函数表,纯虚函数

    1.多态性   指相同对象收到不同消息或不同对象收到相同消息时产生不同的实现动作. C++支持两种多态性:编译时多态性,运行时多态性.    a.编译时多态性:通过重载函数实现 ,模板(2次编译)  ...

  9. Effective C++ Item 41 了解隐式接口和编译期多态

    本文为senlie原创,转载请保留此地址:http://blog.csdn.net/zhengsenlie 经验:class 和 templates 都支持接口和多态. 对 classes 而言接口是 ...

随机推荐

  1. quantmod

    -quantmod(数据和图形) -TTR(技术分析) -blooter(账户管理) -FinancialInstrument(金融产品) -quantstrast(策略模型) -Performanc ...

  2. Golang(六)time 包的用法整理

    1. 常用结构体 Duration:type Duration int64,时间长度,对应单位包括 Nanosecond(纳秒).Microsecond(微妙).Millisecond(毫秒).Sec ...

  3. 由浅入深了解NB-IoT | 我的物联网成长记

    [摘要] 什么是NB-IoT?NB-IoT有什么优势?NB-IoT能做什么?本文将会从NB-IoT技术的发展历程,技术特点,通信协议,应用场景等方面为您全方面解读NB-IoT技术,了解NB-IoT的独 ...

  4. php filter_var()

    定义和用法 filter_var() 函数通过指定的过滤器过滤变量. 如果成功,则返回已过滤的数据,如果失败,则返回 false. 语法 filter_var(variable, filter, op ...

  5. 分享一个Linux C++消息通信框架TCPSHM

    由于本人从事行业关系,Linux环境下的低延迟通信是我关注的技术之一.要达到极端的低延迟,当然同机器内IPC比网络通信快,而Linux IPC方式中无疑是共享内存延迟最低.不过相对于TCP这种通用的通 ...

  6. QT之类型转换

    Qt在进行数据类型转换时,容易忘记如何使用,或者是早已厌倦了百度QString转QByteArray,QByteArray转char,QString转string....... 现在分享一篇QT数据类 ...

  7. 报错 xxx@1.0.0 dev D:\> webpack-dev-server --inline --progress --configbuild/webpack.dev.conf.js

    是因为node_modules有意外改动,导致依赖库不完整. 解决:1.删除项目下的node_modules,在你的项目目录下 重新执行npm install,这会重新生成node_modules, ...

  8. 【题解】Luogu P4284 [SHOI2014]概率充电器

    原题传送门 我们知道,每个电器充电对充电电器数的贡献都是相等的1,所以若第\(i\)个电器有\(p_i\)的概率充电时 \[E=\sum_{i=1}^np_i\] 我们考虑如何求\(p_i\),根据树 ...

  9. Java学习:方法重载的使用规则

    方法的重载 对于功能类似的方法来说,因为参数列表不一样,却需要记住那多不同的方法名称,太麻烦. 方法的重载(Overload):多个方法的名称一样,但是参数列表不一样.好处:只需要记住唯一一个方法名称 ...

  10. <More Effective C#: 改善C#代码的50个有效方法>中文版翻译答疑

    最近, 有一本很赞的.NET技术书中文版出版了 - <More Effective C#: 改善C#代码的50个有效方法>.    从广州\西安\长沙\上海等各地.NET俱乐部都收到反馈, ...