前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Cpp虚函数相关知识点

Cpp虚函数相关知识点

作者头像
yifei_
发布2022-11-14 14:22:47
3700
发布2022-11-14 14:22:47
举报
文章被收录于专栏:yifei的专栏

什么是虚函数?析构函数什么时候该声明为虚函数?什么是虚基类?

虚函数

虚函数是Cpp用来实现多态的一种机制,但如何理解多态呢?人要工作,人派生出多个子类后,一个作家工作就是写文章,一个程序员工作却是写代码。工作的执行者不同,工作的内容也不同。 在类中成员函数前面加一个virtual,这个函数就变成了虚函数。如下代码:

代码语言:javascript
复制
class base{
public:
    virtual void print(){
        cout<<"this is base class"<<endl;
    }
};

那么怎样让它起作用呢?当子类child继承base的时候,创建一个base类的指针,让它 指向子类对象,这时候用基类指针调用print(),此时会自动判断,然后调用子类中的函数, 如果基类不加virtual,基类指针就直接调用基类的print了。代码如下:

代码语言:javascript
复制
#include <iostream>

using namespace std;

class base{
public:
    virtual void print(){
        cout<<"this is base class"<<endl;
    }
};

class child:public base{
public:
    void print(){
        cout<<"this is child class"<<endl;
    }
};

int main()
{
    child c;
    base *ptr=&c;
    ptr->print();
    return 0;
}

在Java中,类中的成员函数默认都是虚函数。

虚函数表

如果想知道是怎么实现的呢,就要说起虚函数表了。 如果一个类中有虚函数,那么编译器会在类的开始位置设置一个虚函数指针,指向一个数组(每一个元素都是函数指针), 这个数组就是虚函数表,它存储着每个虚函数的地址。 当子类继承基类的时候,也会连这个虚函数表一块继承过来,然后把里面的函数指针改成子类中的虚函数地址。 由于使用虚函数会导致建立虚函数表,所以会使程序内存消耗变大,效率降低。 可以看这一篇文章探索C++虚函数在g++中的实现,讲的很详细。

析构函数为什么是虚函数

明白了虚函数的特点,这个问题就不难了。 当基类指针指向子类对象的时候,在对象使用完毕需要释放时,肯定需要调用子类对象的析构函数呀,所以这种情况下析构函数也得是虚函数。 也可以看这段话:

代码语言:javascript
复制
基类指针可以指向派生类的对象(多态性),如果删除该指针delete []p;
就会调用该指针指向的派生类析构函数,而派生类的析构函数又自动调用
基类的析构函数,这样整个派生类的对象完全被释放。
如果析构函数不被声明成虚函数,则编译器实施静态绑定,
在删除基类指针时,只会调用基类的析构函数而不调用派生类析构函数,
这样就会造成派生类对象析构不完全。所以,将析构函数
声明为虚函数是十分必要的。

虚基类

先看实现形式:

代码语言:javascript
复制
class Base{
public:
    virtual void print(){
        cout<<"this is Base class"<<endl;
    }
};

class Child1:virtual public Base{
public:
    void print(){
        cout<<"this is Child1 class"<<endl;
    }
};

当Child1虚继承Base时,就说Base是Child1的虚基类。 那这样做能解决什么问题呢? 有这样一种情况: 首先我们知道,当子类继承父类的时候,子类中会有父类的成员的一份拷贝。 当Child1,Child2继承Base,然后grandson又多继承自两个Child时,grandson中 就会有Child1,Child2,Base中的多份成员了。但是我们其实根本不需要这么多份 拷贝。先看代码:

代码语言:javascript
复制
#include <iostream>

using namespace std;

class Base{
public:
    int a=1;
};

class Child1:public Base{
public:
};

class Child2:public Base{
public:
};

class Grandson:public Child1,public Child2{
public:
    void print(){
        cout<<a<<endl;//此行报错
    }
};

int main()
{
    Grandson grandson;
    grandson.print();
    return 0;
}

当我们使用虚继承的时候,就不会有多份拷贝了:

代码语言:javascript
复制
#include <iostream>

using namespace std;

class Base{
public:
    int a=1;
};

class Child1:virtual public Base{
public:
};

class Child2:virtual public Base{
public:
};

class Grandson:public Child1,public Child2{
public:
    void print(){
        cout<<a<<endl;
    }
};

int main()
{
    Grandson grandson;
    grandson.print();
    return 0;
}

输出结果:

代码语言:javascript
复制
1

当使用多重继承的时候很容出现二义性问题,一般不推荐使用多重继承。 Java中就直接删除了多重继承的特性。

纯虚函数

代码语言:javascript
复制
virtual <类型><函数名>(<参数表>)=0;

在许多情况下,在基类中不能对虚函数给出有意义的实现,而把它声明为纯虚函数,它的实现留给该基类的派生类去做。这就是纯虚函数的作用。 纯虚函数会让类成为抽象类,即不能被实例化。

  • 声明为虚函数只是要求子类必须给出实现,但基类中也可以写出纯虚函数的实现。 析构函数可以是纯虚函数。

参考

如有错误,还请指正。 欢迎与我分享你的看法。 转载请注明出处:http://taowusheng.cn/

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2019-05-18,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 虚函数
  • 虚函数表
  • 析构函数为什么是虚函数
  • 虚基类
  • 纯虚函数
  • 参考
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档