存储在存储器中的对象的功能在哪里?

时间:2014-07-01 07:40:19

标签: c++ memory

假设我们有一个班级:

class Foo
{
private:
    int a;
public:
    void func()
    {
       a = 0;
       printf("In Func\n");
    }
}

int main()
{
    Foo *foo = new Foo();
    foo->func();
    return 0;
}

当创建并初始化类Foo的对象时,我理解整数a将占用4个字节的内存。该功能如何存储?调用foo-> func()时,内存/堆栈/寄存器/程序计数器会发生什么?

3 个答案:

答案 0 :(得分:15)

简答:无论创建的类的实例数是多少,它都只存储在二进制文本或代码部分中一次。

对于每个类的实例,函数不会单独存储在任何位置。它们的处理方式与任何其他非成员函数的处理方式相同。唯一的区别是编译器实际上向函数添加了一个额外的参数,这是类类型的指针。 例如,编译器将生成如下函数原型:

void func(Foo* this);

(请注意,这可能不是最终签名。根据包括编译器在内的各种因素,最终签名可能更加神秘)

对成员变量的任何引用都将替换为

this-><member> //for your ex. a=0 translates to this->a = 0;

所以行foo-&gt; func();大致翻译为:

  1. 将Foo *的值推入堆栈。 #Compiler dependent
  2. 调用func,这将导致指令指针跳转到可执行文件中的func偏移量#architecture dependent Read this and this
  3. Func将从堆栈中弹出值。对成员变量的任何进一步引用之前都将取消引用此值

答案 1 :(得分:10)

您的函数不是虚函数,因此静态调用:编译器将跳转插入与您的函数对应的代码段。每个实例不使用额外的内存。

如果你的函数是虚拟的,你的实例将携带一个vpointer,它将被取消引用以找到它的类'vtable,然后将其编入索引以找到要调用的函数指针,最后跳转到那里。因此额外的成本是每个类一个vtable(可能是一个函数指针的大小,乘以你的类的虚函数的数量),每个实例有一个指针。

请注意,这是虚拟调用的常见实现,但标准并没有强制执行,因此它实际上根本不能实现,但您的机会非常好。如果编译时知道了实例的静态类型,编译器通常也可以完全绕过虚拟调用系统。

答案 2 :(得分:2)

成员函数就像常规函数一样,它们存储在“代码”或“文本”部分中。 (非静态)成员函数有一个特殊之处,那就是传递给函数的“隐藏”this参数。因此,在您的情况下,foo中的地址将传递给func

该参数究竟是如何传递的,寄存器和堆栈的变化由ABI(应用程序二进制接口)定义,并且因处理器而异。除非您告诉我们编译器,操作系统和处理器的组合正在使用(并假设信息随后公开可用 - 并非所有编译器/操作系统供应商都会非常清楚地说明这一点),因此没有严格的定义。例如,x86-64将在WIndows上使用RCX表示this,在Linux上使用RDI,并且调用指令会自动将返回地址压入堆栈。在ARM处理器上[在Linux中,但我认为在Windows中同样适用,我从来没有看过],R0用于this指针,以及用于调用的BX指令,作为一部分它本身存储lr并返回指令的pc。然后必须lrfunc保存[{1}},因为它会调用printf