深入解析C++虚函数表(vtable)和虚指针(vptr)

2b71dd67fb5b474fadb3f966385938ee.webp

在C++中,虚函数表(vtable)和虚指针(vptr)是实现多态性的关键机制。

1. 虚函数表(vtable)和虚指针(vptr)的基本概念

每个使用虚函数的类都有一个虚函数表(vtable)。vtable是一个包含指向类虚函数的指针的数组。每个对象都有一个虚指针(vptr),指向其类的vtable。当调用对象的虚函数时,实际上是通过vptr和vtable来动态确定要调用哪个函数。

示例代码:

#include <iostream>
using namespace std;

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

class Derived : public Base {
public:
    void show() override { cout << "Derived class show" << endl; }
};

int main() {
    Base *bptr;
    Derived d;
    bptr = &d;
    
    // 动态调用正确的函数(Derived::show)
    bptr->show();
    
    return 0;
}

2. 如何通过虚函数实现多态

C++中的多态性允许通过基类指针调用派生类的函数。这是通过虚函数(在基类中声明为virtual)和自动维护的vptr和vtable实现的。

示例代码:

#include <iostream>
using namespace std;

class Animal {
public:
    virtual void speak() { cout << "Some animal sound" << endl; }
};

class Dog : public Animal {
public:
    void speak() override { cout << "Woof" << endl; }
};

class Cat : public Animal {
public:
    void speak() override { cout << "Meow" << endl; }
};

void makeAnimalSpeak(Animal* animal) {
    animal->speak();
}

int main() {
    Dog dog;
    Cat cat;
    
    makeAnimalSpeak(&dog);
    makeAnimalSpeak(&cat);
    
    return 0;
}

3. 虚析构函数

使用虚析构函数确保当通过基类指针删除派生类对象时,可以正确地调用派生类的析构函数,避免资源泄露。

示例代码:

#include <iostream>
using namespace std;

class Base {
public:
    virtual ~Base() { cout << "Base destructor" << endl; }
};

class Derived : public Base {
public:
    ~Derived() { cout << "Derived destructor" << endl; }
};

int main() {
    Base *b = new Derived();
    delete b; // 正确调用Derived析构函数,然后是Base析构函数
    return 0;
}

4. 纯虚函数和抽象类

纯虚函数是一种没有实现的虚函数,其唯一目的是在派生类中进行重写。包含纯虚函数的类称为抽象类,不能实例化。

示例代码:

#include <iostream>
using namespace std;

class Shape {
public:
    virtual void draw() = 0; // 纯虚函数
};

class Circle : public Shape {
public:
    void draw() override { cout << "Drawing Circle" << endl; }
};

int main() {
    // Shape s; // 错误:不能实例化抽象类
    Circle c;
    c.draw(); // 正确:调用Circle::draw
    return 0;
}

5. 重写 vs 重载虚函数

虚函数可以在派生类中被重写,这是多态的基础。重写是指派生类提供一个与基类虚函数签名完全相同的函数实现。与之相对的,重载是在同一个类中创建相同名称但参数列表不同的函数。

示例代码:

#include <iostream>
using namespace std;

class Base {
public:
    virtual void func() { cout << "Base func()" << endl; }
    virtual void func(int x) { cout << "Base func(int)" <<

 endl; }
};

class Derived : public Base {
public:
    void func() override { cout << "Derived func()" << endl; }
    // 注意:这不是重写,因为参数列表不同,这是重载
    void func(int x) override { cout << "Derived func(int)" << endl; }
};

int main() {
    Derived d;
    d.func();       // 调用Derived::func()
    d.func(10);     // 调用Derived::func(int)
    
    return 0;
}

 

最近更新

  1. TCP协议是安全的吗?

    2024-03-17 14:08:02       16 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-03-17 14:08:02       16 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-03-17 14:08:02       15 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-03-17 14:08:02       18 阅读

热门阅读

  1. L1-080 乘法口诀数列(PTA)

    2024-03-17 14:08:02       20 阅读
  2. 备战蓝桥杯Day28 - 拼接最大数字问题

    2024-03-17 14:08:02       22 阅读
  3. 大数据开发(Hive面试真题-卷三)

    2024-03-17 14:08:02       23 阅读
  4. redis的安装,启动和关闭

    2024-03-17 14:08:02       19 阅读
  5. 最新2024年项目基金撰写与技巧及GPT融合应用

    2024-03-17 14:08:02       19 阅读
  6. Android中使用intent可以传递哪些类型的数据?

    2024-03-17 14:08:02       16 阅读
  7. Docker安装kafka

    2024-03-17 14:08:02       21 阅读
  8. Vue生命周期

    2024-03-17 14:08:02       18 阅读
  9. 22.4 docker

    2024-03-17 14:08:02       20 阅读