虚表/虚指针

转载自:http://blog.sina.com.cn/s/blog_6215fe350100fg9i.html

虚函数在c++中的实现机制就是用虚表和虚指针,但是具体是怎样的呢?从more effecive c++其中一篇文章里面可以知道:是每个类用了一个虚表,每个类的对象用了一个虚指针。具体的用法如下:

class A
{
public:
virtual void f();
virtual void g();
private:
int a
};

class B : public A
{
public:
void g();
private:
int b;
};

//A,B的实现省略

  因为A有virtual void f(),和g(),所以编译器为A类准备了一个虚表vtableA,内容如下:

A::f 的地址
 A::g 的地址

B因为继承了A,所以编译器也为B准备了一个虚表vtableB,内容如下:

A::f 的地址
B::g 的地址

注意:因为B::g是重写了的,所以B的虚表的g放的是B::g的入口地址,但是f是从上面的A继承下来的,所以f的地址是A::f的入口地址。

然后某处有语句 B bB;的时候,编译器分配空间时,除了A的int a,B的成员int b;以外,还分配了一个虚指针vptr,指向B的虚表vtableB,bB的布局如下:

vptr : 指向B的虚表vtableB

int a: 继承A的成员

int b: B成员


当如下语句的时候:
A *pa = &bB;

pa的结构就是A的布局(就是说用pa只能访问的到bB对象的前两项,访问不到第三项int b)

那么pa->g()中,编译器知道的是,g是一个声明为virtual的成员函数,而且其入口地址放在表格(无论是vtalbeA表还是 vtalbeB表)的第2项,那么编译器编译这条语句的时候就如是转换:call *(pa->vptr)[1](C语言的数组索引从0开始哈~)。

这一项放的是B::g()的入口地址,则就实现了多态。(注意bB的vptr指向的是B的虚表vtableB)

另外要注意的是,如上的实现并不是唯一的,C++标准只要求用这种机制实现多态,至于虚指针vptr到底放在一个对象布局的哪里,标准没有要求,每个编译器自己决定。

虚指针的大小由系统决定,32bit系统虚指针大小为4。还有计算sizeof(A)时切记要内存对齐。

### C++ 中多继承下虚表指针的工作原理 在C++中,当涉及到多继承特别是存在虚函数的情况下,编译器会为每一个含有虚函数的类创建一张虚拟表(vtable),并给该类的对象分配一个指向这张表的指针虚表指针(vptr)[^1]。 对于普通的单继承结构而言,子类仅需维护单一的`vptr`即可;然而,在多继承场景里,如果多个父类都定义了自己的虚函数,则派生类对象内部可能会有不止一条路径通往这些基类。为了处理这种情况,派生类可能需要保存多个`vptr`来分别对应不同基类所关联的虚函数列表[^4]。 具体来说,在一个多继承体系中: - 如果两个或更多直接基类各自拥有自己的虚函数集,那么派生类实例将会包含相应数量的`vptr`成员变量; - 这些额外的`vptr`通常被放置于由特定基类部分组成的子对象之前或者之后的位置上; - 当通过某个具体的基类视角访问派生类时,程序会选择合适的`vptr`来进行动态绑定操作,从而调用到正确的重写版本的方法[^2]。 此外值得注意的是,当涉及虚继承时,为了避免重复存储共同祖先的数据成员以及解决二义性问题,还会引入特殊的机制——虚基类表及其对应的指针(`vbptr`)用于管理这种复杂的层次关系[^3]。 ```cpp // 示例代码展示了一个简单的多继承案例 #include <iostream> class Base { public: virtual void show() const { std::cout << "Base\n"; } }; class Derived1 : public Base { public: virtual void show() const override { std::cout << "Derived1\n"; } }; class Derived2 : public Base { public: virtual void show() const override { std::cout << "Derived2\n"; } }; class MultiDerived : public Derived1, public Derived2 {}; int main(){ MultiDerived obj; // 输出显示了如何根据上下文选择不同的虚函数实现 static_cast<Derived1*>(&obj)->show(); // 应输出 Derived1 static_cast<Derived2*>(&obj)->show(); // 应输出 Derived2 return 0; } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值