c++虚函数以及虚继承图示

本文探讨了C++中多继承和虚基类的实现机制,详细分析了一般单一继承、一般多继承及虚基类多继承的情况,并通过实例展示了不同类型的继承在内存布局上的区别。

1. 一般单一继承



只有一个虚表指针,虚表包含所有的虚函数

思考:1. 如果将GrandChild中的h_grandchild前面的virtual去掉,内存中的唯一变化是虚表缺少一项GrandChild::h_grandchild

2.  如果是child: virtual public parent?

output:

addr:0014FA44 sizeof = 28
0:
f1783c = f110c8
GrandChild::g_child()//这个时候要注意,虚函数表里没有f//可以这样理解,虚函数表内的函数和非虚拟继承是一样的,但是基类的位置不一样
Child::h_child()
GrandChild::h_grandchild()
1:
f1784c = fffffffc
 fffffffc
 10
2:
100
3:
1000
4:
0
5:
f17828 = f11041
GrandChild::f()
 Parent::g()
 Parent::h()
6:
10

虚基类肯定是放在最后,但是虚函数表中的内容就比较纠结,只放自己被重载的函数。

child和grandchild的指针地址相同,parent的指针地址在最后


2. 一般多继承



3. 虚基类的多继承


请参考:http://blog.youkuaiyun.com/sunmenggmail/article/details/7744608


附上本人检测内存分布的函数:


typedef void (*Fun)(void);
    GrandChild b;
    int *ptr;
    /*B b;*/
    ptr = (int*)&b;
    cout <<"addr:" << ptr << " sizeof = " << sizeof(GrandChild) <<endl;
    Fun pFun = NULL;
   /* Fun pFun = (Fun ) ((int*)ptr[0])[0];
    pFun();*/
    cout<<"parent:"<<(Parent *)&b<<endl<<"child:"<<(Child *)&b<<endl<<"grandchild:"<<&b<<endl;
    for(int i=0;i<sizeof(GrandChild)/sizeof(int);i++)
    {
        cout<<i<<":"<<endl;
        if(ptr[i] < 10000)
        {
            cout << dec << ptr[i]<<endl;
        }
        else
        {
            cout << hex << ptr[i] <<" = " << hex << * ((int*)(ptr[i])) <<endl;
            int *p = (int*)(ptr[i]);
            if(*p==-4)
            {
                cout<<" "<<p[0]<<endl;
                cout<<" "<<p[1]<<endl;
            }
            else
            {
                while(*p!=0)
                {
                    pFun = (Fun )(*p);
                    //cout<<" "<<;
                    //cout.flush();
                    pFun();

                    ++p;
                }
            }
        }
    }


本文中的图片均来自:http://blog.youkuaiyun.com/haoel

<think>嗯,用户问的是“C++虚函数表属于某个类吗?”这个问题。首先,我需要确认用户对虚函数表的理解程度。虚函数表C++实现多态的核心机制之一,但可能用户对它的存储位置和归属有些疑问。 用户可能是在学习C++的面向对象特性,或者在研究底层实现机制时遇到了这个问题。他们可能想知道虚函数表是类的一部分还是对象的一部分,或者它在内存中的分布情况。此外,用户可能对虚函数表的作用和生命周期不太清楚,需要进一步解释。 接下来,我需要回忆虚函数表的基本概念。每个有虚函数的类都有一个虚函数表,它是一个数组,里面存放着虚函数的指针。对象内部有一个指向这个表的指针(vptr)。当调用虚函数时,程序通过vptr找到虚函数表,再根据偏移量调用对应的函数。 然后,用户的问题明确问到虚函数表是否属于某个类。根据C++的机制,虚函数表是类级别的,而不是对象级别的。也就是说,同一个类的所有对象共享同一个虚函数表。因此,虚函数表属于类,而不是单个对象。 不过,可能用户会混淆虚函数表和虚基类表,或者对继承情况下的虚函数表结构不清楚。比如,当有多个基类时,每个基类可能有自己的虚函数表。这时候需要解释继承层次中的虚函数表布局。 另外,用户可能想知道虚函数表的存储位置。通常,虚函数表存储在程序的只读数据段(如.rodata段),而对象中的vptr指向这个表。这涉及到内存布局的问题,可能需要进一步说明。 还要考虑用户可能的误解,比如认为虚函数表是动态生成的,或者每个对象都有独立的虚函数表。需要澄清这些点,强调虚函数表的共享性和静态特性。 最后,要确保回答准确且易懂,使用示例代码和内存布局图示来辅助说明,帮助用户直观理解。同时,可能需要指出虚函数表的实现细节可能因编译器而异,但标准保证其行为一致。</think>是的,C++中的虚函数表(vtable)**属于类(class)**,而不是类的实例对象。它是类的静态属性,所有该类的对象共享同一个虚函数表虚函数表C++实现多态(运行时动态绑定)的核心机制之一。 --- ### 🔍 虚函数表的本质 1. **类级别属性**: - 每个包含虚函数的类(或其派生类)都有一个唯一的虚函数表。 - 虚函数表在编译时生成,存储在程序的只读数据段(如`.rodata`段)。 - 所有该类的对象通过虚函数表指针(vptr)共享同一张表。 2. **对象的虚函数表指针(vptr)**: - 每个对象内部隐式包含一个指向其所属类虚函数表的指针(通常位于对象内存布局的最开始位置)。 - 当调用虚函数时,程序通过 `vptr + 函数偏移量` 找到实际调用的函数。 --- ### 🧱 示例代码与内存布局 ```cpp #include <iostream> class Base { public: virtual void foo() { std::cout << "Base::foo\n"; } virtual void bar() { std::cout << "Base::bar\n"; } }; class Derived : public Base { public: void foo() override { std::cout << "Derived::foo\n"; } }; int main() { Base b; Derived d; return 0; } ``` #### 内存布局(简化示意): ``` 类 Base 的虚函数表: [ &Base::foo, &Base::bar ] 类 Derived 的虚函数表: [ &Derived::foo, &Base::bar ] 对象 b 的内存: [ vptr_base → Base 的虚函数表地址, ... ] 对象 d 的内存: [ vptr_derived → Derived 的虚函数表地址, ... ] ``` --- ### 📌 关键特性 | 特性 | 说明 | |------|------| | **共享性** | 同一类的所有对象共享同一张虚函数表。 | | **继承关系** | 派生类会继承基类的虚函数表,并覆盖重写的函数指针。 | | **运行时多态** | 通过 `vptr + 函数偏移量` 动态绑定到实际函数。 | | **性能开销** | 虚函数调用需要两次间接寻址(`vptr` → 表 → 函数地址)。 | --- ### 📚 虚函数表的实现细节 1. **编译器行为**: - 编译器会为每个包含虚函数的类生成虚函数表。 - 如果派生类重写基类虚函数,表中对应条目会被替换。 - 如果派生类新增虚函数,表中会追加新的函数指针。 2. **多继承的复杂性**: - 多继承时,每个基类子对象会有独立的虚函数表和 `vptr`。 - 示例: ```cpp class A { virtual void foo() {} }; class B { virtual void bar() {} }; class C : public A, public B {}; ``` 对象 `C` 会有两个 `vptr`,分别指向 `A` 和 `B` 的虚函数表。 3. **虚基类与虚继承**: - 虚继承时,虚基类的共享部分通过虚基类表(vbtable)管理,与虚函数表不同。 --- ### 🛠️ 验证虚函数表的存在 可以通过指针操作验证虚函数表的存在(仅用于理解,实际开发中不推荐): ```cpp #include <iostream> typedef void(*FuncPtr)(); int main() { Base obj; // 获取对象的虚函数表指针(假设 vptr 在对象首地址) long** vptr = reinterpret_cast<long**>(&obj); // 获取虚函数表地址 long* vtable = *vptr; // 调用第一个虚函数(Base::foo) FuncPtr func = reinterpret_cast<FuncPtr>(vtable[0]); func(); // 输出 "Base::foo" return 0; } ``` --- ### ✅ 总结 - **虚函数表属于类**,而非对象。 - 所有对象通过 `vptr` 共享该表,实现多态调用。 - 虚函数表的布局和行为由编译器实现,标准仅保证其语义正确性。 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值