c++对象模型-虚函数总结

一.虚函数指针位置分析:

1. 基本概念

  • 虚函数表生成机制:当类中包含虚函数时,编译器会为该类生成一个虚函数表(vtbl)
  • 对象内存布局:每个对象实例会包含一个虚函数表指针(vptr),指向该类的虚函数表起始地址
  • 指针大小:在VS2017环境下,vptr占用4字节空间(Linux环境下为8字节)
#include "pch.h"
#include <iostream>
using namespace std;

class A
{
public:
    int i; //4字节
    virtual void testfunc() {}  //虚函数,vptr4字节。
};

int main()
{
    //虚函数表指针位置分析
    //类:有虚函数,这个类会产生一个虚函数表。
    //类对象,有一个指针,指针(vptr)会指向这个虚函数表的开始地址。
    A aobj;
    int ilen = sizeof(aobj);
    cout << ilen << endl;  //8字节

    char *p1 = reinterpret_cast<char *>(&aobj); //类型转换,硬转 &aobj这是对象aobj的首地址。
    char *p2 = reinterpret_cast<char *>(&(aobj.i));
    if (p1 == p2) //说明aobj.i和aobj的位置相同,说明i在对象aobj内存布局的上边。虚函数表指针vptr在下边
    {
        cout << "虚函数表指针位于对象内存的末尾" << endl;
    }
    else
    {
        cout << "虚函数表指针位于对象内存的开头" << endl;
    }
          


    return 1; 
}

2. 指针位置验证实验

  • 实验设计:
    • 创建包含虚函数和成员变量的测试类
    • 通过比较对象首地址和成员变量地址判断vptr位置
    • 使用sizeof验证对象总大小(VS中为8字节:4字节vptr+4字节int)
  • 关键代码:
3. 内存布局分析
  • 验证方法:
    • 将对象地址强制转换为char*获取首地址(p1)
    • 获取成员变量地址强制转换为char*(p2)
    • 比较p1和p2的地址关系
  • 结论:
    • 当p1 != p2时,说明vptr位于对象内存布局的开头
    • 在VS2017和Linux环境下验证结果一致
    • 成员变量位于vptr之后的内存空间 
4. 跨平台差异
  • 指针大小差异:
    • Windows平台:指针4字节(32/64位程序可能不同)
    • Linux平台:指针8字节(64位系统)
  • 布局一致性:
    • 虽然指针大小不同,但vptr位置规律相同
    • 都位于对象内存布局的开头位置
  • 注意事项:
    • 实际开发中需要考虑平台差异带来的对象大小变化
    • 内存对齐规则可能影响最终对象大小
5. 关键结论
  • 核心发现:
    • 虚函数表指针始终位于对象内存布局的最前端
    • 成员变量紧随其后排列
    • 图示中的虚函数表位置需要修正为上方的正确布局
  • 应用价值:
    • 理解此布局有助于调试内存相关问题
    • 为后续多重继承、虚继承的内存布局学习奠定基础
    • 解释多态调用的底层机制

二、知识小结

知识点

核心内容

考试重点/易混淆点

难度系数

虚函数表指针的存在

类中包含虚函数时,编译器会为该类生成虚函数表(vtable),对象中存储指向该表的指针(vptr)。

vptr的存储位置(对象内存开头/末尾)因编译器而异,需通过调试验证。

⭐⭐

对象内存布局分析

通过sizeof和地址转换验证对象内存结构:

- vptr(4字节,Windows)

- 成员变量(如int i)

Windows vs Linux指针大小差异:

- Windows:4字节

- Linux:8字节

⭐⭐⭐

虚函数表指针位置验证

实验方法:

1. 比较对象首地址与成员变量地址

2. 若地址相同→vptr在末尾;若不同→vptr在开头

结论:

在Windows/Linux平台,vptr均位于对象内存开头。

⭐⭐⭐⭐

跨平台差异

指针大小差异导致sizeof结果不同:

- Windows:vptr+int=8字节

- Linux:vptr+int=16字节

易混淆点:

内存对齐规则不影响vptr位置,仅影响总大小。

⭐⭐

 

 

 二. 继承关系作用下虚函数的手工调用

#include "pch.h"
#include <iostream>
using namespace std;

//父类
class Base
{
public:
    virtual void f() { cout << "Base::f()" << endl; }
    virtual void g() { cout << "Base::g()" << endl; }
    virtual void h() { cout << "Base::h()" << endl; }
};
class Derive :public Base 
{
    virtual void g() { cout << "Derive::g()" << endl; }
};

int main()
{
    //继承关系作用下虚函数的手工调用            
    cout << sizeof(Base) << endl;    
    cout << sizeof(Derive) << endl;

    Derive *d = new Derive(); //派生类指针。
    long *pvptr = (long *)d;  //指向对象的指针d转成了long *类型。
    long *vptr = (long *)(*pvptr); //(*pvptr) 表示pvptr指向的对象,也就是Derive本身。Derive对象是4字节的,代表的是虚函数表指针地址。

    for (int i = 0; i <= 4; i++) //循环5次;
    {
        printf("vptr[%d] = 0x:%p\n", i, vptr[i]);
    }

    typedef void(*Func)(void); //定义一个函数指针类型
    Func f = (Func)vptr[0]; //f就是函数指针变量。 vptr[0]是指向第一个虚函数的。
    Func g = (Func)vptr[1];
    Func h = (Func)vptr[2];
    /*Func i = (Func)vptr[3];
    Func j = (Func)vptr[4];*/

    f();
    g();
    h();
    //i();

    Base *dpar = new Base();
    long *pvptrpar = (long *)dpar;
    long *vptrpar = (long *)(*pvptrpar);

    for (int i = 0; i <= 4; i++) //循环5次;
    {
        printf("vptr Base[%d] = 0x:%p\n", i, vptrpar[i]);
    }

    Func fpar = (Func)vptrpar[0]; 
    Func gpar = (Func)vptrpar[1];
    Func hpar = (Func)vptrpar[2];

    cout << "--------------------" << endl;
    fpar(); 
    gpar();
    hpar();

    return 1; 
}

知识点

核心内容

关键代码/操作

内存布局观察

虚函数表指针验证

通过sizeof验证含虚函数的类对象包含4字节虚表指针(32位系统)

sizeof(Base)/sizeof(Derive)输出4

父类/子类对象首4字节存储虚表指针地址

手工调用虚函数

通过指针转换获取虚表地址,并直接调用虚函数条目

long* vptr = (long*)*(long*)d;

((void(*)())vptr[0])();

虚表条目按声明顺序排列(f→g→h)

继承下的虚函数覆盖

子类覆盖g()时,虚表中对应条目替换为子类实现地址

Derive::g()覆盖Base::g()

父类虚表g()地址≠子类虚表g()地址

非法调用风险

越界访问虚表条目(如vptr[3])导致程序崩溃

尝试调用vptr[3]触发访问异常

虚表末尾以0x00000000标记结束

父类/子类虚表对比

未覆盖的虚函数(f/h)在父子类虚表中地址相同

Base::f地址=Derive::f地址

子类虚表复用父类实现的条目

三.虚函数表分析

#include "pch.h"
#include <iostream>
using namespace std;

//父类
class Base
{
public:
    virtual void f() { cout << "Base::f()" << endl; }
    virtual void g() { cout << "Base::g()" << endl; }
    virtual void h() { cout << "Base::h()" << endl; }
};
class Derive :public Base 
{
    virtual void g() { cout << "Derive::g()" << endl; }
    /* void f() { cout << "Derive::f()" << endl; }
     void g() { cout << "Derive::g()" << endl; }
     void h() { cout << "Derive::h()" << endl; }*/

};

int main()
{
    ////继承关系作用下虚函数的手工调用            
    //cout << sizeof(Base) << endl;    
    //cout << sizeof(Derive) << endl;

    //Derive *d = new Derive(); //派生类指针。
    //Derive *d2 = new Derive(); //派生类指针。

    //long *pvptr = (long *)d;  //指向对象的指针d转成了long *类型。
    //long *vptr = (long *)(*pvptr); //(*pvptr) 表示pvptr指向的对象,也就是Derive本身。Derive对象是4字节的,代表的是虚函数表指针地址。

    //long *pvptr2 = (long *)d2;
    //long *vptr2 = (long *)(*pvptr2);


    //for (int i = 0; i <= 4; i++) //循环5次;
    //{
    //    printf("vptr[%d] = 0x:%p\n", i, vptr[i]);
    //}

    //typedef void(*Func)(void); //定义一个函数指针类型
    //Func f = (Func)vptr[0]; //f就是函数指针变量。 vptr[0]是指向第一个虚函数的。
    //Func g = (Func)vptr[1];
    //Func h = (Func)vptr[2];
    ///*Func i = (Func)vptr[3];
    //Func j = (Func)vptr[4];*/

    //f();
    //g();
    //h();
    ////i();

    //Base *dpar = new Base();
    //long *pvptrpar = (long *)dpar;
    //long *vptrpar = (long *)(*pvptrpar);

    //for (int i = 0; i <= 4; i++) //循环5次;
    //{
    //    printf("vptr Base[%d] = 0x:%p\n", i, vptrpar[i]);
    //}

    //Func fpar = (Func)vptrpar[0]; 
    //Func gpar = (Func)vptrpar[1];
    //Func hpar = (Func)vptrpar[2];

    //cout << "--------------------" << endl;
    //fpar(); 
    //gpar();
    //hpar();

    //(1)一个类只有包含虚函数才会存在虚函数表,同属于一个类的对象共享虚函数表,但是有各自的vptr(虚函数表指针),当然所指向的地址(虚函数表首地址)相同。
    //(2)父类中有虚函数就等于子类中有虚函数。话句话来说,父类中有虚函数表,则子类中肯定有虚函数表。因为你是继承父类的。
    //也有人认为,如果子类中把父类的虚函数的virtual去掉,是不是这些函数就不再是虚函数了?
    //只要在父类中是虚函数,那么子类中即便不写virtual,也依旧是虚函数。
    //但不管是父类还是子类,都只会有一个虚函数表,不能认为子类中有一个虚函数表+父类中有一个虚函数表,
         //得到一个结论:子类中有两个虚函数表。
    //子类中是否可能会有多个虚函数表呢?后续我们讲解这个事;

    //(3)如果子类中完全没有新的虚函数,则我们可以认为子类的虚函数表和父类的虚函数表内容相同。
     //但,仅仅是内容相同,这两个虚函数表在内存中处于不同位置,换句话来说,这是内容相同的两张表。
    //虚函数表中每一项,保存着一个虚函数的首地址,但如果子类的虚函数表某项和父类的虚函数表某项代表同一个函数(这表示子类没有覆盖父类的虚函数),
        //则该表项所执行的该函数的地址应该相同。
    //(4)超出虚函数表部分内容不可知;

    typedef void(*Func)(void); //定义一个函数指针类型

    Derive derive;
    long *pvptrderive = (long *)(&derive);  
    long *vptrderive = (long *)(*pvptrderive); //0x00b09b6c {project100.exe!void(* Derive::`vftable'[4])()} {11538847}
    Func f1 = (Func)vptrderive[0]; //0x00b0119f {project100.exe!Base::f(void)}
    Func f2 = (Func)vptrderive[1]; //0x00b0150f {project100.exe!Derive::g(void)}
    Func f3 = (Func)vptrderive[2]; //0x00b01325 {project100.exe!Base::h(void)}
    Func f4 = (Func)vptrderive[3]; //0x69726544
    Func f5 = (Func)vptrderive[4]; //0x3a3a6576

    Derive derive2 = derive; //调用拷贝构造函数
    long *pvptrderive2 = (long *)(&derive2);
    long *vptrderive2 = (long *)(*pvptrderive2);

    Base base = derive; //直接用子类对象给父类对象值,子类中的属于父类那部分内容会被编译器自动区分(切割)出来并拷贝给了父类对象。
                        //所以Base base = derive;实际干了两个事情:
                                //第一个事情:生成一个base对象
                                //第二个事情:用derive来初始化base对象的值。
                                  //这里编译器给咱们做了一个选择,显然derive初始化base对象的时候,
                                    //derive的虚函数表指针值并没有覆盖base对象的虚函数表指针值,编译器帮我们做到了这点;
    long *pvptrbase = (long *)(&base);
    long *vptrbase = (long *)(*pvptrbase); //0x00b09b34 {project100.exe!void(* Base::`vftable'[4])()} {11538847}
    Func fb1 = (Func)vptrbase[0];   //0x00b0119f {project100.exe!Base::f(void)}
    Func fb2 = (Func)vptrbase[1];   //0x00b01177 {project100.exe!Base::g(void)}
    Func fb3 = (Func)vptrbase[2];   //0x00b01325 {project100.exe!Base::h(void)}
    Func fb4 = (Func)vptrbase[3];    //0x00000000
    Func fb5 = (Func)vptrbase[4];    //0x65736142

                         
    //OO(面向对象)  和OB(基于对象)概念:
    //c++通过类的指针和引用来支持多态,这是一种程序设计风格,这就是我们常说的面向对象。object-oriented model;
    //OB(object-based),也叫ADT抽象数据模型【abstract datatype model】,不支持多态,执行速度更快,因为
           //因为 函数调用的解析不需要运行时决定(没有多态),而是在编译期间就解析完成,内存空间紧凑程度上更紧凑,因为没有虚函数指针和虚函数表这些概念了;
    //Base *pbase = new Derive();
    //Base &base2 = derive2;
    //但显然,OB的设计灵活性就差;
    //c++既支持面向对象程序设计(继承,多态)(OO),也支持基于对象(OB)程序设计。


            

    return 1; 
}

四:多重继承虚函数表分析

#include "pch.h"
#include <iostream>
using namespace std;

//基类1
class Base1
{
public:
    virtual void f()
    {
        cout << "base1::f()" << endl;
    }
    virtual void g()
    {
        cout << "base1::g()" << endl;
    }
};

//基类2
class Base2
{
public:
    virtual void h()
    {
        cout << "base2::h()" << endl;
    }
    virtual void i()
    {
        cout << "base2::i()" << endl;
    }
};

//子类
class Derived :public Base1, public Base2
{
public:
    virtual void f() //覆盖父类1的虚函数
    {
        cout << "derived::f()" << endl;
    }
    virtual void i() //覆盖父类2的虚函数
    {
        cout << "derived::i()" << endl;
    }

    //如下三个我们自己的虚函数
    virtual void mh() 
    {
        cout << "derived::mh()" << endl;
    }
    
    virtual void mi()
    {
        cout << "derived::mi()" << endl;
    }

    virtual void mj()
    {
        cout << "derived::mj()" << endl;
    }
};



int main()
{
    // 多重继承虚函数表分析
    //多重继承
    cout << sizeof(Base1) << endl;
    cout << sizeof(Base2) << endl;
    cout << sizeof(Derived) << endl;

    Derived ins; 
    Base1 &b1 = ins; //多态
    Base2 &b2 = ins;
    Derived &d = ins;

    typedef void(*Func)(void);
    long *pderived1 = (long *)(&ins);
    long *vptr1 = (long *)(*pderived1); //取第一个虚函数表指针。

    long *pderived2 = pderived1 + 1; //跳过4字。
    long *vptr2 = (long *)(*pderived2); //取第二个虚函数表指针。

    Func f1 = (Func)vptr1[0]; //0x00ab15d7 {project100.exe!Derived::f(void)}
    Func f2 = (Func)vptr1[1]; //0x00ab15f0 {project100.exe!Base1::g(void)}
    Func f3 = (Func)vptr1[2]; //0x00ab15cd {project100.exe!Derived::mh(void)}
    Func f4 = (Func)vptr1[3]; //0x00ab15ff {project100.exe!Derived::mi(void)}
    Func f5 = (Func)vptr1[4]; //0x00ab15eb {project100.exe!Derived::mj(void)}
    Func f6 = (Func)vptr1[5]; //非正常
    Func f7 = (Func)vptr1[6];
    Func f8 = (Func)vptr1[7];
    
    Func f11 = (Func)vptr2[0]; //0x00ab15af {project100.exe!Base2::h(void)}
    Func f22 = (Func)vptr2[1]; //0x00ab15b9 {project100.exe!Derived::i(void)}
    Func f33 = (Func)vptr2[2]; //非正常
    Func f44 = (Func)vptr2[3];

    b1.f();
    b2.i();
    d.f();
    d.i();
    d.mh();
    d.g();

    //----------------
    cout << "-----------------" << endl;
    f1();
    f2();
    f3();
    f4();
    f5();
    cout << "-------------" << endl;
    f11();
    f22();

    //说明
    //(1)一个对象,如果它的类有多个基类则有多个虚函数表指针(注意是两个虚函数表指针,而不是两个虚函数表);
    //在多继承中,对应各个基类的vptr按继承顺序依次放置在类的内存空间中,且子类与第一个基类共用一个vptr(第二个基类有自己的vptr);

    //(2)老师画图:适合vs2017。
    //(2.1)子类对象ins有里那个虚函数表指针,vptr1,vptr2
    //(2.2)类Derived有两个虚函数表,因为它继承自两个基类;
    //(2.3)子类和第一个基类公用一个vptr(因为vptr指向一个虚函数表,所以也可以说子类和第一个基类共用一个虚函数表vtbl),
        //因为我们注意到了类Derived的虚函数表1里边的5个函数,而g()正好是base1里边的函数。
    //(2.4)子类中的虚函数覆盖了父类中的同名虚函数。比如derived::f(),derived::i();

       
    

    return 1; 
}

五:辅助工具:

#include "pch.h"
#include <iostream>
using namespace std;

//基类1
class Base1
{
public:
    virtual void f()
    {
        cout << "base1::f()" << endl;
    }
    virtual void g()
    {
        cout << "base1::g()" << endl;
    }
};

//基类2
class Base2
{
public:
    virtual void h()
    {
        cout << "base2::h()" << endl;
    }
    virtual void i()
    {
        cout << "base2::i()" << endl;
    }
};

//子类
class Derived :public Base1, public Base2
{
public:
    virtual void f() //覆盖父类1的虚函数
    {
        cout << "derived::f()" << endl;
    }
    virtual void i() //覆盖父类2的虚函数
    {
        cout << "derived::i()" << endl;
    }

    //如下三个我们自己的虚函数
    virtual void mh()
    {
        cout << "derived::mh()" << endl;
    }

    virtual void mi()
    {
        cout << "derived::mi()" << endl;
    }

    virtual void mj()
    {
        cout << "derived::mj()" << endl;
    }
};



int main()
{    
    //多重继承
    cout << sizeof(Base1) << endl;
    cout << sizeof(Base2) << endl;
    cout << sizeof(Derived) << endl;

    Derived ins;
    Base1 &b1 = ins; //多态
    Base2 &b2 = ins;
    Derived &d = ins;

    typedef void(*Func)(void);
    long *pderived1 = (long *)(&ins);
    long *vptr1 = (long *)(*pderived1); //取第一个虚函数表指针。

    long *pderived2 = pderived1 + 1; //跳过4字。
    long *vptr2 = (long *)(*pderived2); //取第二个虚函数表指针。

    Func f1 = (Func)vptr1[0]; //0x00ab15d7 {project100.exe!Derived::f(void)}
    Func f2 = (Func)vptr1[1]; //0x00ab15f0 {project100.exe!Base1::g(void)}
    Func f3 = (Func)vptr1[2]; //0x00ab15cd {project100.exe!Derived::mh(void)}
    Func f4 = (Func)vptr1[3]; //0x00ab15ff {project100.exe!Derived::mi(void)}
    Func f5 = (Func)vptr1[4]; //0x00ab15eb {project100.exe!Derived::mj(void)}
    Func f6 = (Func)vptr1[5]; //非正常
    Func f7 = (Func)vptr1[6];
    Func f8 = (Func)vptr1[7];

    Func f11 = (Func)vptr2[0]; //0x00ab15af {project100.exe!Base2::h(void)}
    Func f22 = (Func)vptr2[1]; //0x00ab15b9 {project100.exe!Derived::i(void)}
    Func f33 = (Func)vptr2[2]; //非正常
    Func f44 = (Func)vptr2[3];

    b1.f();
    b2.i();
    d.f();
    d.i();
    d.mh();
    d.g();

    //----------------
    cout << "-----------------" << endl;
    f1();
    f2();
    f3();
    f4();
    f5();
    cout << "-------------" << endl;
    f11();
    f22();

    //第五节  辅助工具,vptr、vtbl创建时机
    //cl.exe:编译链接工具
    // cl /d1 reportSingleClassLayoutDerived project100.cpp
    // g++ -fdump-class-hierarchy -fsyntax-only 3_4.cpp

    //vptr(虚函数表指针)什么时候创建出来的?
    //vptr跟着对象走,所以对象什么时候创建出来,vptr就什么时候创建出来。运行的时候;
    //实际上,对于这种有虚函数的类,在编译的时候,编译器会往相关的构造函数中增加 为vptr赋值的代码,这是在编译期间编译器为构造函数增加的。
     //这属于编译器默默为我们做的事,我们并不清楚。
    //当程序运行的时候,遇到创建对象的代码,执行对象的构造函数,那么这个构造函数里有 给对象的vptr(成员变量)赋值的语句,自然这个对象的vptr就被赋值了;

    //虚函数表是什么时候创建的?
    //实际上,虚函数表是编译器在编译期间(不是运行期间)就为每个类确定好了对应的虚函数表vtbl的内容。
     //然后也是在编译器期间在相应的类构造函数中添加给vptr赋值的代码,这样程序运行的时候,当运行到成成类对象的代码时,会调用类的构造函数,执行到类的构造
       //函数中的 给vptr赋值的代码,这样这个类对象的vptr(虚函数表指针)就有值了;









    return 1;
}

六:单纯的类不纯时引发的虚函数调用问题

#include "pch.h"
#include <iostream>
#include <time.h >
using namespace std;

class X
{
public:
    int x;
    int y;
    int z;
    //X() :x(0), y(0), z(0)
    X()
    {
        //编译器角度 伪码;
        //vptr = vtbl; //下边的memset会把vptr(虚函数表指针)清0

        memset(this, 0, sizeof(X));
        cout << "构造函数被执行" << endl;
    }
    //X(const X &tm) :x(tm.x), y(tm.y), z(tm.z)
    X(const X &tm)
    {
        memcpy(this, &tm, sizeof(X));
        cout << "拷贝构造函数被执行" << endl;
    }
    virtual ~X()
    {
        cout << "析构函数被执行" << endl;
    }
    virtual void virfunc()
    {
        cout << "虚函数virfunc()被执行" << endl;
    }
    void ptfunc()
    {
        cout << "普通函数ptfunc()被执行" << endl;
    }
};
int main()
{    
    //第六节  单纯的类不纯时引发的虚函数调用问题
    // 单纯的类:比较简单的类,尤其不包含 虚函数和虚基类。
    //X x0;  //调用构造函数
    ///*x0.x = 100;
    //x0.y = 200;
    //x0.z = 300;*/
    //x0.virfunc(); //虚函数表指针为null居然可以成功调用虚函数;

    //X x1(x0); //调用拷贝构造函数
    //cout << "x1.x=" << x1.x << " x1.y=" << x1.y << " x1.z=" << x1.z << endl;

    //如果类并不单纯,那么在构造函数中使用如上所示的memset或者拷贝构造函数中使用如上所示的memcpy方法,那么就会出现程序崩溃的情形;
    //那就是某些情况下,编译器会往类内部增加一些我们看不见 但真实存在的成员变量(隐藏成员变量),有了这种变量的类,就不单纯了;
    //同时,这种隐藏的成员变量的 增加(使用) 或者赋值的时机,往往都是在 执行构造函数或者拷贝构造函数的函数体之前进行。
    //那么你如果使用memset,memcpy,很可能把编译器给隐藏变量的值你就给清空了,要么覆盖了;

    //比如你类中增加了虚函数,系统默认往类对象中增加 虚函数表指针,这个虚函数表指针就是隐藏的成员变量。

    //X *px0 = new X();
    //px0->ptfunc(); //正常调用
    //px0->virfunc(); //无法正常调用
    //delete px0; //无法正常调用
    //new出来的对象,虚函数变得无法正常执行了;

    //对多台,虚函数,父类子类。虚函数,主要解决的问题父类指针指向子类对象这种情况。
    //只有虚函数,没有继承,那么虚函数和普通函数有啥区别呢? 老师认为此时就没啥时机区别。


    int i = 9;
    printf("i的地址 = %p\n", &i);
    X x0;
    printf("ptfunc()的地址=%p\n", &X::ptfunc); //打印正常的成员函数地址。
    //long *pvptrpar = (long *)(&x0);
    //long *vptrpar = (long *)(*pvptrpar);
    //printf("virfunc的地址 = %p\n", vptrpar[1]);//虚函数virfunc地址
    x0.ptfunc();
    x0.virfunc(); //不叫多态,属于静态联编
    //我们推断:这个函数ptfunc()和virfunc()函数,是在编译的就确定好的;
    //静态联编 和动态联编。
    //静态联编 :我们编译的时候就能确定调用哪个函数。把调用语句和倍调用函数绑定到一起;
    //动态联编:是在程序运行时,根据时机情况,动态的把调用语句和被调用函数绑定到一起,动态联编一般旨有在多态和虚函数情况下才存在。

    X *pX0 = new X();
    pX0->ptfunc();
    pX0->virfunc(); //通过虚函数表指针,找虚函数表,然后从虚函数表中找到virfunc虚函数的地址并调用。

    //更明白:虚函数,多态,这种概念专门给指针或者引用用的;
    X &xy = *pX0;
    xy.virfunc();
    X &xy2 = x0;
    xy2.virfunc();


    return 1;
}

 

posted @ 2025-07-04 11:43  白伟碧一些小心得  阅读(23)  评论(0)    收藏  举报