从编译器角度理解虚函数和继承

本文从编译器的角度解析C++中的虚函数和继承。通过一个例子展示了当使用不同类型的指针调用同一对象的不同函数时,编译器如何处理虚函数与非虚函数的调用,强调了虚函数的动态绑定特性及其在多态中的作用。

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

在c++中,继承的概念可以理解为c中得嵌套结构体,对于各种函数,类中的成员函数,类中的友元函数,各种继承的虚拟函数,只要从编译器的角度去理解

就会变得简单。例如下例:

class D{
 public:
     int func_D();
     int v_func_DD();
};

class D1 : public D{
 public:
     int func_D1();
     virtual int v_func_DD();
};

class D2 : public D1{
 public:
     int func_D2();
     int v_func_DD();
     int d1;
};

//....

  函数的具体实现略

//....

 

int main(int argc, char **argv){

 D2 dd2;
 D *d_ptr;
 D1 *d1_ptr;
 D2 *d2_ptr;


 d_ptr = &dd2;
 d1_ptr = &dd2;
 d2_ptr = &dd2;

 

 d_ptr->v_func_DD();//①
 d1_ptr->v_func_DD();//②

 

d1_ptr->func_D2();//③

d2_ptr->func_D2();//④


 return 0;
}

 

在主函数中,只有一个对象实例,即class D2类型的dd2;D2类直接继承自D1,间接继承自D,因为可以用这两种类型的指针引用D2类型的对象实例。

从编译器角度来看时,对于①处,编译器将做如下处理:

 1.判断指针类型,发现d_ptr的类型是class D。

 2. 在class D中寻找 v_func_D函数的定义,如果没有定义,将报错。

 3. 如果有一个定义,判断是不是虚函数。

    3.1 如果不是虚函数(正如本例那样),则在全局符号表中找到相应的修饰后符号,假设为v_func_DD_classD_void,并根据符号对应的函数地址调用该函数。

    3.2 如果在class D中v_func_DD被定义为虚函数,则需要动态绑定。
于是,在①处,由于v_func_DD并没有在class D中被定义为虚拟函数,则编译器将判断该调用为int D::v_func_DD(),这里与调用一个全局函数并无区别。

 

对于②处的函数调用,编译器将会做出相同处理:

 1.判断指针类型,发现d1_ptr的类型是class D1。

 2. 在class D1中寻找 v_func_D函数的定义,如果没有定义,将报错。

 3. 如果有一个定义,判断是不是虚函数。

    3.1 如果不是虚函数,则在全局符号表中找到相应的修饰后符号,此时为虚函数,跳过此步。

    3.2 在class D1中v_func_DD被定义为虚函数,则需要动态绑定,在动态绑定时,由于d1_ptr绑定的对象是D2类型,而此时D2类型的虚函数表已经建立好,并填入了

         int D2::v_func_DD()的地址,所以这里调用的真正函数是int D2::v_func_DD()。

 

对于③处的处理,编译器判断指针类型d1_ptr为class D1,而D1中并没有func_D2的定义,即是d1_ptr绑定的是D2对象,编译器也是不允许这种情况的,所以编译器将报

未定义的错误。

 

对于④的处理,由于指针是class D2类型,因此会正确调用int D2::func_D2()函数,这是,调用该成员函数和调用一个全局函数的情况并无区别,只不过编译器根据类型名,

参数名,和函数名对最后生成的全局符号进行了mangling。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值