文章目录

  • 一、虚函数
  • 二、纯虚函数
  • 三、虚析构函数
  • 四、虚函数与纯虚函数用法与区别

关于C++中面向对象的多态特性,多态:即多种形态。在C++中一般是这么解释的:向不同的对象发送同一个消息,不同的对象在接收时会产生不同的行为。
成员函数的重载,或者运算符的重载,模板函数还有下面要讲的虚函数,都算是多态性的一种体现。

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

举个现实中的例子,我们还拿学生来做举例:说上课了,那么不同的学生可能会走进不同的教室,上课了,有的学生上语文课,有的学生上数学课,等等。所以针对同一个上课的消息,不同的学生产生的行为是不一样的。

一、虚函数

虚函数这个东西可以说是C++面向对象的一个非常重要的概念。
他的核心思想就是一句话:使用基类之指针,指向派生类的对象,调用虚函数的时候,最后调用的是派生类的函数!
虚函数:
①、分别给 CStudent、CXiaoStudent、CZhongStudent 三个类添加 shangke() 这么个成员函数;
②、分别在三个类的 shangke 函数中打印出不同的文本内容,以示区别;
③、测试如下代码:

int main(int argc, char* argv[])
{CZhongStudent stud_zhong;stud_zhong.shangke();     // 使用的是 CZhongStudent::shangke()函数CStudent *pStud = &stud_zhong;pStud->shangke();        // 使用的是 CStudent::shangke()函数return 0;
}

上述基类的指针 pStud 指向了派生类的对象,但是本身基类也有同名该函数,所以就调用了跟指针同类型的基类的 CStudent::shangke() 的函数了。
④、把 CStudent 的 shangke 函数声明成 virtual 虚函数,如下:
virtual void shangke()
{
cout << “CStudent::shangke called.” << endl;
}
两次调用的都是 CZhongStudent::shangke 函数。这就是 virtual 虚函数的妙用!

虚函数的注意事项:
①、virtual 只能用来声明类的成员函数,把它作为虚函数,而不能将类作用域外的普通函数声明成虚函数。因为虚函数的作用是允许在派生类中对基类的虚函数重新定义。显然,他只能用于类的继承层次结构中;
②、一个类中的某个函数被声明成虚函数之后,同一类中就不能再定义一个非virtual的参数和返回值类型都相同的成员函数了。

使用虚函数
一般情况下是某个函数所在的类可能会作为父类/基类,而且该函数有可能会被派生类重写,并被派生类使用,那么这个时候就可以考虑将该函数声明为 virtual 虚函数。否则就不用!因为声明成虚函数之后是有开销的,所以不要随随便便的想声明成虚函数就声明。

二、纯虚函数

纯虚函数就是没有函数体的虚函数。包含纯虚函数的类就叫抽象类不能生成独立的对象)。下面的类 A 就是一个抽象类:

class A {private:int a;
public:virtual void Print() = 0;       //纯虚函数void fun1() { cout << "fun1"; }
};

Print 就是纯虚函数。纯虚函数的写法就是在函数声明后面加=0,不写函数体。纯虚函数实际上是不存在的,引入纯虚函数是为了便于实现多态。
既然抽象类不能用来生成独立对象,但是抽象类可以作为基类,用来派生新类。可以定义抽象类的指针或引用,并让它们指向或引用抽象类的派生类的对象,这就为多态的实现创造了条件。独立的抽象类的对象不存在,但是被包含在派生类对象中的抽象类的对象是可以存在的。

我们设计了一款“魔法门”游戏,其中 CCreature 类的写法如下:
实际上不需要独立的 CCreature 对象,因为一个怪物对象要么代表“狼”,要么代表“龙”,要么代表“雷鸟”,总之是代表一种具体的怪物,而不会只是一个抽象的、什么都不是的“怪物”类的对象。所以,上面的 CCreature 类中的 Attack、Hurted、FightBack 成员函数也都没有实际操作。
既然如此,将上面三个成员函数声明为纯虚函数,从而把 CCreature 类变成一个抽象类,就是很恰当的了。因此,CCreature 类的改进写法如下:

class CCreature {                      //“怪物”类
protected:int lifeValue, power;
public:virtual void Attack(CCreature* p) = 0;virtual void Hurted(int nPower) = 0;virtual void FightBack(CCreature* p) = 0;
};

几何形体对象要么是圆形,要么是三角形,要么是矩形,等等,也不存在抽象的 CShape 类的对象,因此 CShape 类应该如下改写为抽象类:

class CShape                         //基类:形体类
{public:virtual double Area() = 0;       //求面积virtual void Printlnfo() = 0;    //显示信息
};

三、虚析构函数

#include <iostream>
using namespace std;
class CShape                       //基类
{public:~CShape() { cout << "CShape::destrutor" << endl; }
};
class CRectangle : public CShape    //派生类
{public:int w, h;                       //宽度和高度~CRectangle() { cout << "CRectangle::destrutor" << endl; }
};
int main()
{CShape* p = new CRectangle;delete p;return 0;
}==========输出结果===============
CShape::destrutor

输出结果说明,delete p;只引发了 CShape 类的析构函数被调用,没有引发 CRectangle 类的析构函数被调用。这是因为该语句是静态联编的,编译器编译到此时,不可能知道此时 p 到底指向哪个类型的对象,它只根据 p 的类型是 CShape * 来决定应该调用 CShape 类的析构函数。

按理说,delete p;会导致一个 CRectangle 类的对象消亡,应该调用 CRectangle 类的析构函数才符合逻辑,否则有可能引发程序的问题。
例如,假设程序需要对 CRetangle 类的对象进行计数,如果此处不调用 CRetangle 类的析构函数,就会导致计数不正确。
再如,假设 CRectangle 类的对象在存续期间进行了动态内存分配,而释放内存的操作都是在析构函数中进行的,如果此处不调用 CRetangle 类的析构函数,就会导致被释放的对象中动态分配的内存以后再也没有机会回收。

综上所述,人们希望delete p;这样的语句能够聪明地根据 p 所指向的对象执行相应的析构函数。实际上,这也是多态。为了在这种情况下实现多态,C++ 规定,需要将基类的析构函数声明为虚函数,即虚析构函数。

改写上面程序中的 CShape 类,在析构函数前加 virtual 关键字,将其声明为虚函数:
class CShape{
public:
virtual ~CShape() { cout << “CShape::destrutor” << endl; }
};
则程序的输出变为:
CRectangle::destrutor
CShape::destrutor

说明 CRetangle 类的析构函数被调用了。实际上,派生类的析构函数会自动调用基类的析构函数。
只要基类的析构函数是虚函数,那么派生类的析构函数不论是否用virtual关键字声明,都自动成为虚析构函数。

一般来说,一个类如果定义了虚函数,则最好将析构函数也定义成虚函数。
析构函数可以是虚函数,但是构造函数不能是虚函数。

四、虚函数与纯虚函数用法与区别

虚函数与纯虚函数不同之处

  1. 虚函数和纯虚函数可以定义在同一个类(class)中,含有纯虚函数的类被称为抽象类(abstract class),而只含有虚函数的类(class)不能被称为抽象类(abstract class)。

  2. 虚函数可以被直接使用,也可以被子类(sub class)重载以后以多态的形式调用,而纯虚函数必须在子类(sub class)中实现该函数才可以使用,因为纯虚函数在基类(base class)只有声明而没有定义。

  3. 虚函数的定义形式:virtual {method body}
    纯虚函数的定义形式:virtual { } = 0;

  4. 虚函数必须实现,如果不实现,编译器将报错,错误提示为:
    error LNK****: unresolved external symbol “public: virtual void __thiscall
    ClassName::virtualFunctionName(void)”

  5. 虚函数是C++中用于实现多态(polymorphism)的机制。核心理念就是通过基类访问派生类定义的
    函数

虚函数与纯虚函数相同之处
6. 虚函数和纯虚函数都可以在子类(sub class)中被重载,以多态的形式被调用。
7. 虚函数和纯虚函数通常存在于抽象基类(abstract base class -ABC)之中,被继承的子类重载,目的是提供一个统一的接口。
8. 在虚函数和纯虚函数的定义中不能有static标识符,原因很简单,被static修饰的函数在编译时候要求前期bind,然而虚函数却是动态绑定(run-time bind),而且被两者修饰的函数生命周期(life recycle)也不一样。
9. 对于虚函数来说,父类和子类都有各自的版本。由多态方式调用的时候动态绑定。
10. 实现了纯虚函数的子类,该纯虚函数在子类中就编程了虚函数,子类的子类即孙子类可以覆盖
该虚函数,由多态方式调用的时候动态绑定。
11. 如果一个类中含有纯虚函数,那么任何试图对该类进行实例化的语句都将导致错误的产生,因为抽象基类(ABC)是不能被直接调用的。必须被子类继承重载以后,根据要求调用其子类的方法。

虚函数和纯虚函数的区别:https://blog.csdn.net/hackbuteer1/article/details/7558868

C++(十五)虚函数和纯虚函数(抽象类)相关推荐

  1. 一口气搞懂《虚函数和纯虚函数》

    学习C++的多态性,你必然听过虚函数的概念,你必然知道有关她的种种语法,但你未必了解她为什么要那样做,未必了解她种种行为背后的所思所想.深知你不想在流于表面语法上的蜻蜓点水似是而非,今天我们就一起来揭 ...

  2. c++ 虚函数,纯虚函数的本质区别

    转载博客:https://mp.weixin.qq.com/s?__biz=MzAxNzYzMTU0Ng==&mid=2651289202&idx=1&sn=431ffd1fa ...

  3. 虚函数和纯虚函数详解

    https://mp.weixin.qq.com/s?__biz=MzAxNzYzMTU0Ng==&mid=2651289202&idx=1&sn=431ffd1fae4823 ...

  4. 虚函数、纯虚函数、虚继承、多继承

    来源:http://www.tnove.com/?p=57 C++的一个特征是多太,其中多态主要表现在 1.编译时多态  函数overload实现 2.运行是多态  虚函数override实现 其中虚 ...

  5. C/C++编程:虚函数与纯虚函数

    虚函数 VS 纯虚函数 虚函数 虚函数是应在派生类中重新定义的函数.当使用指针或者对基类的引用来引用派生类的对象时,可以为该对象调用虚函数并执行该派生类的版本. 虚函数的"虚",虚 ...

  6. C++学习12:C++多态、虚函数、虚析构函数、纯虚函数、抽象类

    一 多态概述 C++中的多态分为静态多态和动态多态.静态多态是函数重载,在编译阶段就能确定调用哪个函数.动态多态是由继承产生的,指同一个属性或行为在基类及其各派生类中具有不同的语义,不同的对象根据所接 ...

  7. 但并不从包含函数声明的接口派生_C++的虚函数和纯虚函数

    虚函数:类成员函数前面添加virtual关键字,则该函数被称为虚函数. 纯虚函数:在虚函数的基础上,在函数末尾加上 = 0. class Animal {public: virtual void Sh ...

  8. C++知识点51——虚函数与纯虚函数(下)

    接上一篇文章https://blog.csdn.net/Master_Cui/article/details/109957146 10.练习 示例 class base { public:base() ...

  9. C++ 在继承中虚函数、纯虚函数、普通函数,三者的区别

    C++ 在继承中虚函数.纯虚函数.普通函数,三者的区别 1.虚函数(impure virtual) C++的虚函数主要作用是"运行时多态",父类中提供虚函数的实现,为子类提供默认的 ...

最新文章

  1. 智源抗疫 - 药物研发小分子性质预测赛
  2. linux pid t 头文件_Linux信号处理
  3. 以太坊知识教程------智能合约(3)函数修饰符
  4. DCMTK:OFtuple的单元测试
  5. 安徽省学考计算机操作,安徽省教育考试院全国计算机等级考试网上报名流程与操作步骤...
  6. 浏览器渲染页面的原理及流程---------重绘与重排(回流)--优化
  7. Audacity Mac版(音频录制编辑合成工具)中文版
  8. 知网下载学位论文PDF版本的一个方法
  9. 风哥Oracle数据库视频培训教程大合集(网盘下载.共18套)
  10. Nod32的内网升级方案
  11. 4. C语言预定义符号
  12. vim配置——MA6174
  13. 手机与个人计算机区别,手机CPU跟电脑CPU有什么差别?两者差距到底有多大?
  14. 当计算机遇上经济学:如何量化你的投资并获得第一桶金
  15. SitePoint博客的3大变化
  16. 触摸识别,智能分拣……看AI如何为垃圾分类赋能
  17. 《缠中说禅108课》41:没有节奏,只有死
  18. go-testify和robfig
  19. [架构之路-26]:目标系统 - 系统软件 - bootloader uboot使用方法、常用命令
  20. SQL注入攻防入门详解

热门文章

  1. 流媒体服务器中的单播点播和广播
  2. 大数据——Java 知识点整理
  3. c++语言教程书本pdf,C++语言基础教程_吕凤翥.pdf
  4. 小米赴港IPO,雷总年薪有多少?| 热点
  5. SDN学习之OpenFlow协议分析
  6. 色戒女主角/汤唯在英国的日子(值得教育孩子)
  7. 头歌题目:计算指定的年月日,是这一年中的哪一天,Python实现
  8. 饿了么薅羊毛时刻正式开启
  9. 四川川之音:抖音电商发布消费者权益保护年报:累计封禁超300万件风险商品
  10. 用 Python 爬了微信好友,原来他们是这样的人...