C++联编与虚函数表

C++联编与虚函数表

1.联编

联编:将源代码中的函数调用解释为执行特定的函数代码块叫函数名联编 也就是说:确定执行函数调用会执行哪一个函数

I>静态联编(早期联编)

编译期间,决定调用哪个函数,普通的非虚函数采用静态联编

II>动态联编(晚期联编)

程序运行期间,决定调用哪个函数,虚函数采用动态联编

2.虚函数机制实现——虚函数表

I>虚函数表的内容

虚函数表是一个函数指针数组数组中存储的指针指向要调用的虚函数

II>存在虚函数的类中的隐藏成员

若一个类中有虚函数,则该类会有一个隐藏的参数(指向虚函数表的指针)

class A1 {

};

class A2 {
public:
    virtual void f() { cout << "A::f()" << endl; }
    virtual void g() { cout << "A::g()" << endl; }
};

int main() {

    cout << "sizeof(A1) : " << sizeof(A1) << endl;
    cout << "sizeof(A2) : " << sizeof(A2) << endl;
    return 0;
}

输出:

sizeof(A1) : 1
sizeof(A2) : 4

类对象单元存储了成员变量,A1由于没有成员变量,在创建对象时,分配1个字节表示一个对象,主要是让这个成员的this指针有指向;A2也没有成员变量,它的对象单元应该也占一个字节,但是,这里却占了4个字节,说明有隐藏的成员变量,这个隐藏的成员就是指向虚函数表的指针

III>虚函数表的更新

 

  1. 派生类会继承基类的虚函数表(基类声明的虚函数,派生类中默认为虚函数)
  2. 派生类如果重定义虚函数,则修改虚函数表的指针
  3. 派生类如果声明新的虚函数,则在虚函数表中添加新的指针

基类指针(或引用)指向派生类对象,可以通过虚函数表,调用派生类对象的虚函数

注意:只能调用派生类中声明过的虚函数,因为虚函数的声明个数决定了虚函数表的长度,不能越界访问

IV>通过虚函数表调用虚函数

声明这样的两个类

class A {
public:
    virtual void f();
    virtual void g();
};
void A::f() { cout << "A::f()" << endl; }
void A::g() { cout << "A::g()" << endl; }

class B : public A {
public:
    void f();
};
void B::f() { cout << "B::f()" << endl; }

通过上面的例子得到当前环境地址所占字节数:4B,也就是说,函数指针也占4B

第一步:获取虚函数表首地址(vptr):
由于,没有其他成员,那么唯一的成员就是虚函数表首地址。
由于,指针占4B,同过int型指针可以取出4B的数据(当前环境int也占4B) 得到vptr的值:

B objb;
int * vptr = (int *)(*(int *)(&objb));
//将objb的地址转为int *,以便取出4个字节的首地址
//通过解引用的到vptr,因为函数指针也占4字节,所以转成int *

第二步:通过虚函数表中的函数地址调用函数:

void(*function)() = (void(*)())vptr[0];
//vptr[0]取出地址
//类型转换为返回值为void的函数指针
//给function赋值

第三部:通过指针调用函数:

for(int i = 0; i < 2; ++i){
    void(*function)() = (void(*)())vptr[i];
    function();
}

//可以直接使用地址调用:  
for(int i = 0; i < 2; ++i)
    ((void(*)())vptr[i])();

猜你喜欢

转载自blog.csdn.net/qq2071114140/article/details/89304660