编译器的这个指针和虚函数的细节

时间:2015-06-10 03:12:06

标签: c++ pointers virtual-functions vtable this-pointer

我正在阅读Bjarne的论文:“Multiple Inheritance for C++”。

在第370页的第3节中,Bjarne说:“编译器将成员函数的调用转换为带有”额外“参数的”普通“函数调用;”额外“参数是指向对象的指针。调用成员函数。“

考虑一个简单的课程A

class A {
    int a;
    void f(int i);
};

成员函数A :: f:

的调用
A* pa;
pa->f(2)

由编译器转换为“普通函数调用”:

f__F1A(pa, 2)

pa作为this指针传递。上面的例子很容易理解。

请考虑以下代码段:

class A {int a; void f(int);};
class B : A {int b; void g(int);};
class C : B {int c; void h(int);};

问题1:

成员函数A :: f:

的调用
C* pc = new C;
pc->g(int)

由编译器转换为“普通函数调用”:

g__G1C(pc, int) or g__G1B((*B)pc, int)

这个指针是* pc还是(* B)pc? 另一个问题是编译如何知道成员函数的位置?

让我们通过添加虚拟关键字使上面的例子更有趣。

class A {
    int a;
    virtual void f(int);
    virtual void g(int);
    virtual void h(int);
};
class B : A {int b; void g(int); };
class C : B {int c; void h(int); };

类c对象C看起来像:

C:

-----------                vtbl:
+0:  vptr -------------->  -----------
+4:  a                     +0: A::f
+8:  b                     +4: B::g
+12: c                     +8: C::h
-----------                -----------  

对虚函数的调用由编译器转换为间接调用。例如,

C* pc;
pc->g(2)

变得像:

(*((*pc)[1]))(pc, 2)

Bjarne的论文容忍了我的上述结论。

问题2:

(1)在vtbl中,我相信这些函数指针是在指定期间分配的  运行时。编译器如何知道第二个函数指针  应该指向B类的g的实现?怎么了 编译数字呢?

(2)在上面的例子中,所有成员都是int,我们假设  编译器为int分配4个字节的内存。如果会员是,该怎么办? char,编译器是否仍然为char分配4个字节的内存?或者只是一个字节?

(3)(*((*pc)[1]))(pc, 2),这里的指针是个人电脑,为什么不呢 (* B)电脑?传递这个指针有什么规则吗?

任何人都可以帮我回答这些问题吗?对此,我真的非常感激。我有一个 明天的截止日期与这些问题有关。请帮忙!!!

1 个答案:

答案 0 :(得分:1)

  

问题1:

     

成员函数A :: f:

的调用
C* pc = new C;
pc->g(int)

这不是对A :: f()的调用。这是对B :: g()的调用。

  

由编译器转换为“普通函数调用”:

g__G1C(pc, int) or g__G1B((*B)pc, int)
  

这个指针是* pc还是(* B)pc?

都不是。这是B*

  

另一个问题是编译如何知道成员函数的位置?

没有。它知道他们的名字。链接器分配它们的地址。

  

问题2:

     

(1)编译器如何知道第二个函数指针应指向B类的g实现?编译器如何计算它?

因为它是C的vtbl,而C继承自B,而B具有最接近的g()定义。

  

(2)在上面的例子中,所有成员都是int,我们假设编译器为int分配4个字节的内存。如果成员是char,编译器是否仍然为char分配4个字节的内存?或者只是一个字节?

这取决于处理器的对齐和打包规则,编译器,编译器选项,周围的#pragmas等。

  

(3)(*((* pc)[1]))(pc,2),这里的这个指针是pc,为什么不(* B)pc?

这个假设与your other question相矛盾。它是B*

  

是否有传递此指针的规则?

见上文。