深入浅出c++中的虚函数

20 阅读2分钟

深入浅出c++中的虚函数

 

以下知识学习自github.com/Light-City/…


#include<iostream>

 

class Base {

public:

       Base() = default;

       virtual void fun()

       {

              std::cout << "Base::fun" << std::endl;

       }

 

       virtual void fun1()

       {

              std::cout << "Base::fun1" << std::endl;

       }

       ~Base() = default;

};

 

class Myfun: public Base

{

public:

       Myfun()=default;

       void fun() override

       {

              std::cout << "Myfun::fun" << std::endl;

       }

       void fun1() override

       {

              std::cout << "Myfun1::fun1" << std::endl;

       }

       ~Myfun()=default;

 

};

 

 

int main()

{

       Base* ptr = new Myfun();

       ptr->fun();

       ptr->fun1();

       return 0;

}

运行结果:


Myfun::fun

Myfun1::fun1

 

从上面的代码可以看出,c++的多态依赖于虚函数,而为了实现虚函数,c++使用一种虚函数表的方式。

 

基础理论

虚函数表是c++编译器在编译时期为每个有虚函数的类生成的一种静态数组。其中存储着指向派生类覆写函数的函数指针。当程序运行时,在创建类对象的过程中就会设置一个指针(vptr)来指向虚函数表。

 

实现过程


Base* ptr = new Myfun();

ptr->fun();

在上面的代码中,程序识别出fun函数是一个虚函数,ptr->vptr会指向Myfun函数的虚函数表,随后分辨出调用的是Myfun的fun函数。


虚函数调用步骤:

1. ptr→ vptr

         ↓

2. vptr → vtable[0]

        ↓

3. 找到Myfun::fun()

什么函数可以声明为虚函数

1.static成员函数不可以声明为虚函数。static成员函数并不属于类对象或类实例,没有this指针。而虚函数则依赖类对象的vptr,vptr在类的构造函数中生成,需要有this指针来访问它。

2.构造函数不可以声明为虚函数。虽然虚函数表在编译器就已经生成,但访问它需要的vptr却是在程序运行时生成,需要构造函数来创建。而此时访问虚函数又需要有vptr。二者相矛盾。

3,析构函数可以声明为虚函数。

我们来看一个例子


#include<iostream>

 

class Base {

public:

       Base() { std::cout << "Base Constructor\n"; }

       ~Base() { std::cout << "Base Destructor\n"; }

};

 

class Myfun:public Base{

public:

       Myfun(){ std::cout << "Myfun Constructor\n"; }

       ~Myfun(){ std::cout << "Myfun Destructor\n"; }

};

 

int main()

{

       Base* base = new Base();

       delete base;

       std::cout << "--------------------" << std::endl;

       Myfun* myfun = new Myfun();

       delete myfun;

       std::cout << "--------------------" << std::endl;

       Base* fun= new Myfun();

       delete fun;

       return 0;

}

结果:


Base Constructor

Base Destructor

--------------------

Base Constructor

Myfun Constructor

Myfun Destructor

Base Destructor

--------------------

Base Constructor

Myfun Constructor

Base Destructor

如图,我们代码中的fun没有调用Myfun的析构函数,这将会导致内存泄漏。此时我们需要在Base的析构函数加上virtual。此时才可以正常调用基类的析构函数。

纯虚函数和抽象类

以下就是纯虚函数的的声明。


class Entity

{

public:

    virtual std::string Getname()=0;

};

纯虚函数会强制继承这个类的子类在内部实现这个函数,同时我们也不能实例化Entity。至少包含一个纯虚函数的类被成为抽象类,而只有纯虚函数(析构函数也可以声明为纯虚函数)的类就是我们常说的c++中的接口类。