Skip to content

虚函数表 Virtual Table

对于一个类来说,如果类中存在虚函数,那么该类的大小就会多4个字节,然而这4个字节就是一个指针的大小,这个指针指向虚函数表。 所以,如果对象存在虚函数,那么编译器就会生成一个指向虚函数表的指针,所有的虚函数都存在于这个表中,虚函数表就可以理解为一个数组,每个单元用来存放虚函数的地址。

cpp
#include <iostream>
#include <stdio.h>
using namespace std;

class Base {
public:
    virtual void a() { cout << "Base a()" << endl; }
    virtual void b() { cout << "Base b()" << endl; }
    virtual void c() { cout << "Base c()" << endl; }
};

class Derive : public Base {
public:
    virtual void b() { cout << "Derive b()" << endl; }
};

int main()
{
    cout << "----------Base-------------" << endl;
    Base* q = new Base;
    long* tmp1 = (long*)q;
    long* vptr1 = (long*)(*tmp1);
    for (int i = 0; i < 3; i++) {
        printf("vptr[%d] : %p\n", i, vptr1[i]);
    }

    Derive* p = new Derive;
    long* tmp = (long*)p;
    long* vptr = (long*)(*tmp);
    cout << "---------Derive------------" << endl;
    for (int i = 0; i < 3; i++) {
        printf("vptr[%d] : %p\n", i, vptr[i]);
    }
    return 0;
}

纯虚析构函数

如果基类并不需要回收清理什么,那么析构函数就可以是虚构函数

虚析构函数

  • 一般的建议是作为基类的析构函数是虚函数

  • 当指针指向的对象是基类类型时,delete销毁对象的时候并不会调用派生类的析构函数,这样就造成了对象销毁不完整

    总结一下虚析构函数的作用:

(1)如果基类的析构函数不加virtual关键字 当基类的析构函数不声明成虚析构函数的时候,当派生类继承父类,基类的指针指向派生类时,delete掉基类的指针,只调动基类的析构函数,而不调动派生类的析构函数。

(2)如果基类的析构函数加virtual关键字 当基类的析构函数声明成虚析构函数的时候,当派生类继承基类,基类的指针指向派生类时,delete掉基类的指针,先调动派生类的析构函数,再调动基类的析构函数。

原理分析

由于基类的析构函数为虚函数,所以派生类会在所有属性的前面形成虚表,而虚表内部存储的就是基类的虚函数。 当delete基类的指针时,由于派生类的析构函数与基类的析构函数构成多态,所以得先调动派生类的析构函数;之所以再调动基类的析构函数,是因为delete的机制所引起的,delete 基类指针所指的空间,要调用基类的析构函数。

虚继承

  • 是指一个指定的基类,在继承体系结构中,将其成员数据实例共享给也从这个基类型直接或间接派生的其它类。
cpp
class typename :virtual public inheritance{

}

纯虚函数

cpp
virtual ReturnType Function(Argument List)=0;

抽象类

  • 含有纯虚函数的类就是抽象类

  • 抽象类没有完整的信息,只能是派生类的基类

  • 抽象类不能有实例,不能有静态成员

  • 派生类应该实现抽象类的所有方法

2021/04/24

cpp
// CMakeProject1.h: 标准系统包含文件的包含文件
// 或项目特定的包含文件。
#pragma once

#include <iostream>
#include <string>

// TODO: 在此处引用程序需要的其他标头。
class Basic {
public:
    Basic(){
        int_data = 0;
        double_data = 0;
        std::cout << __FUNCTION__ << std::endl;
    }
    virtual ~Basic() {
        std::cout << __FUNCTION__ << std::endl;
    }
     virtual void Function() {
        std::cout << __FUNCTION__ << std::endl;
    }
     void Function_2() {
         std::cout << __FUNCTION__ << std::endl;
     }
private:
    int int_data;
    double double_data;
};

class Superior :virtual public Basic{
public:
    Superior() {
        std::cout << __FUNCTION__ << std::endl;
    }
    void Function() override{
        std::cout << __FUNCTION__ << std::endl;
    }
    void Function_2() {
        std::cout << __FUNCTION__ << std::endl;
    }
    Basic* get_basic()
    {
        return this;
    }
    ~Superior() {
        std::cout << __FUNCTION__ << std::endl;
    }
};

// CMakeProject1.cpp: 定义应用程序的入口点。

#include "CMakeProject1.h"
using namespace std;

int main()
{
    Superior* sup = new Superior;
    sup->get_basic()->Function();
    sup->Function();
    sup->get_basic()->Function_2();
    sup->Function();
    delete sup;
    cout << "--------------------" << endl;
    Basic* base = new Superior;
    base->Function();
    delete base;
    cout << "--------------------" << endl;
    Superior sup_2;
    Basic* base_2 = &sup_2;
    base_2->Function();
    base_2->Function_2();
    return 0;
}

结果如下

cpp
Basic::Basic
Superior::Superior
Superior::Function
Superior::Function
Basic::Function_2
Superior::Function
Superior::~Superior
Basic::~Basic
--------------------
Basic::Basic
Superior::Superior
Superior::Function
Superior::~Superior
Basic::~Basic
--------------------
Basic::Basic
Superior::Superior
Superior::Function
Basic::Function_2
Superior::~Superior
Basic::~Basic