c ++类实例成员如何在机器级别交付?

时间:2014-05-26 07:26:15

标签: c++ class pointers

我理解类实例成员的基本布局(给定一个典型的c ++实现),但是假设你有一个带有int num作为成员的MyClass,并且你创建了它的实例,该成员的特定地址如何在内存中在运行时处理? 我将通过一个例子更加清楚:

class MyClass
    {
      int num;
      int num2;
      int num3;
    public:
      void setNum(); //always sets num to 10
    };

然后你调用setnum,它如何知道设置为10的内存? MyClass的内存布局可能类似于

class MyClass size(12):
        +---
     0  | num
     4  | num1
     8  | num2
        +---

因此,当使用隐藏指向myclass实例的隐藏指针进行成员访问时,它是如此简单吗?它是基于offest编写的?例如myclasspointer + 4?

编辑澄清它是如何决定写入的?失败的copypaste离开了那里的vftable。我完全想象它只是一个已知的偏移对吗?

还是更复杂的东西?

为不明确的术语道歉我很少知道如何正确地表达一个问题...

1 个答案:

答案 0 :(得分:2)

编译器将知道class(或struct)的内容,最重要的是知道不同成员变量的偏移量。 setNum函数被赋予this指针作为"隐藏"参数,编译器将采用this变量并添加num的偏移量。

究竟如何发生这取决于编译器。在LLVM中,它将使用getelementptr VM指令,该指令理解结构,并且在给定基址的情况下,添加索引给出的偏移量。然后,这将转换为某种指令,它接受this并在单个指令中添加直接偏移量,或者两条指令加载指针然后添加偏移量 - 取决于架构的一点点,以及接下来的说明是什么"需要"。

由于num成员是结构中的第一个成员,它将为零,因此在使用clang ++ -O1编译的x86-64上,我们得到了这个反汇编:

_ZN7MyClass6setNumEv:                   # @_ZN7MyClass6setNumEv
    movl    $10, (%rdi)
    retq

换句话说,将数字10移动到this的地址中(在%rdi - Linux机器上的第一个参数中)。

LLVM IR更好地展示了发生的事情:

%class.MyClass = type { i32, i32, i32 }

; Function Attrs: nounwind uwtable
define void @_ZN7MyClass6setNumEv(%class.MyClass* nocapture %this) #0 align 2 {
entry:
  %num = getelementptr inbounds %class.MyClass* %this, i64 0, i32 0
  store i32 10, i32* %num, align 4, !tbaa !1
  ret void
}

该类包含3个i32(32位整数),该函数采用this指针,然后使用getelementptr获取第一个元素(元素0)。是的,还有一个比你预期更多的争论。这就是LLVM的工作原理;)

然后将值10的商店指令转换为%num计算的地址。

如果我们更改setNum中的代码,以便将其存储到num2,我们会得到:

define void @_ZN7MyClass6setNumEv(%class.MyClass* nocapture %this) #0 align 2 {
entry:
  %num2 = getelementptr inbounds %class.MyClass* %this, i64 0, i32 2
  store i32 10, i32* %num2, align 4, !tbaa !1
  ret void
}

请注意将最后一个号码更改为getelementptr

作为汇编代码,它变为:

_ZN7MyClass6setNumEv:                   # @_ZN7MyClass6setNumEv
    movl    $10, 8(%rdi)
    retq

(目前看来,在原始问题的修订版2中,您的类MyClass的大小为12,3 * 4字节,而不是像文本所说的那样8)。