C++ 虚函数(试验富文本编辑器)

发布于:2022-12-18 ⋅ 阅读:(312) ⋅ 点赞:(0)

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

文章目录

前言

情景

使用步骤

总结



前言

首先我们先复习一下C++的大特性:

  • 封装:封装就是将数据和行为(或功能)有机结合起来,形成一个整体。把数据和处理数据的操作结合形成类,数据和函数都是类的成员。增强安全性和简化编程。对外隐藏实现细节暴露公共接口。外部通过接口来调用。
  • 继承:继承就是A类被B类继承,A类为父类,B类为子类。B类继承Al类的所有公共和保护成员数据(属性)和成员函数(方法)。子类可以重新定义父类某些属性,重写父类的某些方法,即覆盖父类的某些属性和方法。使其获得与父类不同的功能。
  • 多态:一个接口多种实现状态。多态的出现大大提高了程序的扩展性。动态多态,是基于封装和继承的来实现的,多个子类对继承于一个父类的虚函数进行重写,来实现不同状态。静态多态分两种,一种在同一个作用域对函数进行重载(函数名相同,函数参数列表不同),另一种是对函数进行模板化,忽略数据类型强调数据操作。

此时多态便引出了本文的核心:虚函数
 


情景

当我们希望定义一个基类和多个派生类。他们都具有同样名字的成员函数,但是实现的细节不同,我们希望一个通用函数根据传入类的对象去调用各类函数。此时我们就需要使用虚函数来实现这个要求。

代码如下:

#include<iostream>
using namespace std;
class Base01  //基类Base01
{
public:
    void display()const
    {
        cout<<"Base01 display now"<<endl;
    }
};

//派生类Base02
class Base02:public Base01
{
public:
    void display()const
    {
        cout<<"Base02 display now"<<endl; //此时对Base1的display函数进行了重载。
    }
};

//公有派生类Derived定义
class Derived:public Base02
{
public:
	void display()const
	{
		cout << "Derived display now" << endl;
	}
};

void fun(Base1* ptr)  //参数为指向基类对象的指针
{
	ptr->display();   //对象指针->成员名
}

int main()
{
	Base1 base1;        //声明Base1类对象
	Base2 base2;        //声明Base2类对象
	Derived derived;    //声明Derived类对象

	fun(&base1);        //用Base1对象的指针调用fun函数
	fun(&base2);        //用Base2对象的指针调用fun函数
	fun(&derived);      //用Derived对象的指针调用fun函数

	return 0;
}

我们希望的输出为:

Base01 display now
Base02 display now
Derived display now

但是实际上结果为:

所以,千万不要重新定义! 

不成功的原因:在编译阶段,编译器根据指针无法判断运行是会指向哪个对象,指针是什么类型的他就会调用对应类型的成员函数,在上面的我们使用的Base01这个类,所以全部都会使用这玩意儿。

使用步骤

既然在编译阶段无法确定要调用的类的函数,可以在运行时再确定——“虚函数

class Base1//基类Base1定义
{
public:
	virtual void display()const
	{
		cout << "Base01 display now" << endl;
	}
};

对基类使用虚函数关键词 virtual 疗效显而易见,结果如下:

 


总结

用 virtual 关键字说明的函数就是虚函数。
虚函数是实现运行时多态性基础(而静态多态性即编译阶段,则靠重载完成)
C++中的虚函数是动态绑定的函数
虚函数必须是非静态的成员函数,虚函数经过派生之后,就可以实现运行过程中的多态

虚函数是靠一张虚表来实现的,称为V-Table。这张表解决了继承、覆盖的问题,保证其容真实反应实际的函数。

析构函数为什么一定要使用虚函数?

由于类的多态性,基类指针可以指向派⽣生类的对象,如果删除该基类的指针,就会调⽤用该指针指向的派⽣生类析构函数,⽽而派⽣生类的析构函数⼜又⾃自动调⽤用基类的析构函数,这样整个派⽣生类的对象完全被释放。如果析构函数不不被声明成虚函数,则编译器器实施静态绑定,在删除基类指针时,只会调⽤用基类的析构函数⽽而不不调⽤用派⽣生类析构函数,这样就会造成派⽣生类对象析构不不完全,造成内存泄漏。所以将析构函数声明为虚函数是⼗十分必要的。在实现多态时,当⽤用基类操作派⽣生类,在析构时防⽌止只析构基类⽽而不不析构派⽣生类的状况发⽣生,要将基类的析构函数声明为虚函数。

举个例子哈:

#include <iostream>
using namespace std;

class Parent{
public:
    Parent(){
        cout << "Parent construct function" << endl;
    };
    //析构函数
    ~Parent(){
        cout << "Parent destructor function" <<endl;
    }
};
class Son : public Parent{
public:
    Son(){
        cout << "Son construct function" << endl;
    };
    ~Son(){
        cout << "Son destructor function" <<endl;
    }
};
int main()
{
    Parent* p = new Son();
    delete p;
    p = NULL;
    return 0;
}

输出为:

将基类的析构函数改为 virtual ~Parent()之后的output为:

 

 


网站公告

今日签到

点亮在社区的每一天
去签到