假设我们有一个班级:
class Foo
{
private:
int a;
public:
void func()
{
a = 0;
printf("In Func\n");
}
}
int main()
{
Foo *foo = new Foo();
foo->func();
return 0;
}
Run Code Online (Sandbox Code Playgroud)
当创建并初始化类Foo的对象时,我理解整数a将占用4个字节的内存.该功能如何存储?调用foo-> func()时,内存/堆栈/寄存器/程序计数器会发生什么?
bas*_*hrc 17
简短的回答:无论创建的类的实例数是多少,它都只存储在二进制文本或代码部分中一次.
对于每个类的实例,函数不会单独存储在任何位置.它们的处理方式与任何其他非成员函数的处理方式相同.唯一的区别是编译器实际上向函数添加了一个额外的参数,这是类类型的指针.例如,编译器将生成如下函数原型:
void func(Foo* this);
Run Code Online (Sandbox Code Playgroud)
(请注意,这可能不是最终签名.根据包括编译器在内的各种因素,最终签名可能更加神秘)
对成员变量的任何引用都将替换为
this-><member> //for your ex. a=0 translates to this->a = 0;
Run Code Online (Sandbox Code Playgroud)
所以行foo-> func(); 大致翻译为:
Que*_*tin 10
您的函数不是虚函数,因此静态调用:编译器将跳转插入与您的函数对应的代码段.每个实例不使用额外的内存.
如果你的函数是虚拟的,你的实例将携带一个vpointer,它将被解引用以找到它的类'vtable,然后将其编入索引以找到要调用的函数指针,最后跳转到那里.因此,额外的成本是每个类一个vtable(可能是一个函数指针的大小,乘以类的虚函数的数量),每个实例一个指针.
请注意,这是虚拟调用的常见实现,但标准并没有强制执行,因此它实际上根本不能实现,但您的机会非常好.如果编译器在编译时了解您的实例的静态类型,编译器通常也可以完全绕过虚拟调用系统.
成员函数就像常规函数一样,它们存储在“代码”或“文本”部分中。(非静态)成员函数有一个特别之处,那就是this传递给函数的“隐藏”参数。因此,在您的情况下,地址foo将被传递到func。
该参数的确切传递方式以及寄存器和堆栈发生的情况由 ABI(应用程序二进制接口)定义,并且因处理器而异。对此没有严格的定义,除非您告诉我们正在使用什么编译器、操作系统和处理器的组合(并假设该信息随后公开 - 并非所有编译器/操作系统供应商都会非常清楚地告诉我们)。例如,x86-64 将在 WIndows 和Linux 上使用RCXfor ,并且调用指令会自动将返回地址压入堆栈。在ARM处理器上[在Linux中,但我认为这同样适用于Windows,我只是从来没有看过],R0用于指针,BX指令用于调用,它作为其本身的一部分存储与要返回的指令。then 必须保存在 中 [可能在堆栈上] ,因为它调用.thisRDIthislrpclrfuncprintf
| 归档时间: |
|
| 查看次数: |
5922 次 |
| 最近记录: |