虚函数表 以及 虚函数表的继承过程

本文详细介绍了C++中的虚函数表和虚表继承机制,包括虚函数表的创建、虚表指针的继承以及虚表的特点。在子类继承父类时,根据是否重写虚函数,虚表指针会指向不同的虚函数地址。同时,文章探讨了同一类对象的虚表指针一致性以及多继承下虚表的特殊情况。最后,讨论了虚表指针和虚函数的存储位置,虚表指针位于对象内存中,虚函数则常驻代码区。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

一、虚函数表 和 虚表继承

1、虚函数表

2、虚表继承

(1) 子类未重写父类虚函数

(2) 子类重写了父类虚函数

二、虚表的特点

1、同一个类的对象的虚表指针相同

2、多继承时子类中的两个父类虚表地址不一样(实际调用是同一个函数)

三、虚表指针和虚函数放在哪??


下面介绍时搭配使用的案例代码如下:

class Person {
public:
    virtual void BuyTicket() { cout << "Person类:买票-全价" << endl; }
};

class Student : public Person {
public:
    // 子类的 virtual 可以不写,但是父类的必须写
    virtual void BuyTicket() { cout << "Student类:买票-半价" << endl; }
};

// 售票窗口
void TicketWindow(Person& p)
{ 
    p.BuyTicket(); 
}

int main(){
     Person ps;
     Student st;
 
     TicketWindow(ps);    // 传递Person对象买成人票
     TicketWindow(st);    // 传递Student对象买半票
}

一、虚函数表 和 虚表继承

1、虚函数表

当子类的某个成员函数是虚函数时,此时子类对象所在空间中,除了已经声明的子类成员外,会多出一个成员,那就是 虚函数表指针_vfptr(简称“虚表指针”),在构造函数初始化阶段才会初始化该虚表指针。虚表指向一个虚函数表,虚函数表中存放了虚函数的地址。(严格来说,这里的虚表指针不是虚函数表的地址,但是为了方便理解,先暂时这样介绍

2、虚表继承

在继承的时候,虚表指针也会被继承。这样的话,就分为了两种情况,一种是子类没有重写父类的虚函数,一种是子类重写了父类的虚函数。

(1) 子类未重写父类虚函数

在解释之前,我们先使用VS中的监视窗口了解,先把上面Student类中的虚函数注释掉。

class Student : public Person {
public:
    //virtual void BuyTicket() { cout << "Student类:买票-半价" << endl; }
};

我们会发现虚函数的地址相同,但是虚函数表的地址不同。这是因为,子类原模原样地继承了父类的虚表指针指向的虚函数,这就是 “虚函数地址是相同的” 的原因;(虚函数表的地址不同,是因为虚表指针本质上是某一句汇编指令的地址,不同类的对象因为最后要调用不同的虚函数,所以执行的指令肯定也不同)

(2) 子类重写了父类虚函数

下面我们同样使用监视窗口来观察。

class Student : public Person {
public:
    virtual void BuyTicket() { cout << "Student类:买票-半价" << endl; }
};

在继承的时候,在代码区产生了新的函数。子类重写了父类虚函数的代码,虽然依然会继承父类的虚表指针,但是虚函数表里写的虚函数地址就不再是父类的虚函数地址了,而是重写以后新的虚函数地址。

二、虚表的特点

1、同一个类的对象的虚表指针相同

假设现在有两个Person 类的对象 ps1 和 ps2 。

int main() {
    Person ps1;
    Person ps2;
       
    return 0;
}

通过监视窗口就会发现,同一个类的对象的虚表指针是相同的。其实上面也提到,虚表指针 __vfptr本质上是 某一句汇编指令的地址,不同类的对象因为要调用不同的虚函数,所以执行的指令肯定是不同的,但是同一个对象调用的虚函数是一样的,所以执行相同的指令,即虚表指针是一样的。

2、多继承时子类中的两个父类虚表地址不一样(实际调用是同一个函数)

我们再新加一个 Teacher 类,然后让Student 类先后继承Person类和Teacher类,然后通过监视窗口看一下Student类的虚表继承情况。

class Teacher {
public:
    virtual void BuyTicket() { cout << "Teacher类:买票-优惠价" << endl; }
};

class Student : public Person, public Teacher {
public:
    virtual void BuyTicket() { cout << "Student类:买票-半价" << endl; }
};

监视窗口的情况如下:

你或许会觉得很奇怪,子类明明重写了父类的虚函数,此时应该换上子类的虚函数地址,也就是说,Person下的虚函数地址 和 Teacher 下的虚函数地址应该是一样的才对。为什么会不一样?

==》其实是一样的,只不过Teacher类在被继承的时候,需要多执行其他的指令,所以这里填的是多执行的指令的地址,实际在调用的时候,会被替换成 和Person 一样的虚函数地址。

三、虚表指针和虚函数放在哪??

虚表指针__vfptr 是对象的一部分,对象在哪,虚表指针就在哪,所以虚表指针在栈上。

因为同一个类的对象指向的是同一个虚函数表,所以虚函数不能因为对象被销毁就被释放了,所以虚函数放在了代码区。(严格来说是在常量区,只不过常量区和代码区被合并了)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值