C ++对象表示

时间:2013-03-03 13:33:08

标签: c++ oop class object memory-management

我有一个疑问:我可以声明一个指向类成员函数的指针

void (*MyClass::myFunc)(void);

我可以声明一个指向类成员变量的指针

int (MyClass::*var);

我的问题是:对象(由成员函数和成员变量组成)如何在内存中构建(asm-level)?

我不确定,因为除了多态和运行时虚函数之外,我甚至可以在没有对象的情况下声明指向成员函数的指针,这意味着代码函数在多个类之间共享(尽管它们需要* this指针正常工作)

但变量怎么样?为什么即使没有对象实例,我也可以声明指向成员变量的指针?当然我需要一个人使用它,但事实上我可以声明一个没有对象的指针,这让我觉得内存中的一个类对象用指向其他内存区域的变量表示它的变量。

我不确定我是否正确解释了我的疑问,如果不是让我知道,我会尝试更好地解释

1 个答案:

答案 0 :(得分:3)

类很简单地存储在内存中 - 几乎与结构一样。如果你检查存储类实例的地方的内存,你会注意到它的字段是一个接一个地打包的。

但是,如果你的班级有虚拟方法,那就有区别了。在这种情况下,存储在类实例中的第一件事是指向虚方法表的指针,它允许虚方法正常工作。您可以在互联网上阅读更多相关信息,这是一个更高级的主题。幸运的是,您不必担心,编译器会为您完成所有操作(我的意思是,处理VMT,而不是担心)。

我们来看看这些方法。当你看到:

void MyClass::myFunc(int i, int j) { }

实际上,编译器会将其转换为:

void myFunc(MyClass * this, int i, int j) { }

当你打电话时:

myClassInstance->myFunc(1, 2);

编译器生成以下代码:

myFunc(myClassInstance, 1, 2);

请记住,这是一个简化 - 有时它比这更复杂(特别是当我们讨论虚拟方法调用时),但它或多或少地显示了编译器如何处理类。如果你使用一些低级调试器,比如WinDbg,你可以检查方法调用的参数,你会看到,第一个参数通常是指向你调用方法的类实例的指针。

现在,所有相同类型的类共享其方法的二进制文件(已编译的代码)。因此,为每个类实例制作副本没有意义,因此内存中只保留一个副本,所有实例都使用它。现在应该很清楚,为什么即使你没有类的实例,你也可以获得指向方法的指针。

但是,如果要调用保存在变量中的方法,则必须提供一个类实例,该实例可以通过隐藏的“this”参数传递。


编辑:回应评论

您可以在another SO question中阅读有关指针成员的更多信息。我猜,指向成员的指针存储了类实例的开头和指定字段之间的差异。当您尝试使用指向成员的指针检索字段的值时,编译器将查找类实例的开头,并按指针到成员中存储的字节数移动以到达指定的字段。

每个类实例都有自己的非静态字段副本 - 否则它们对我们来说没什么用处。

注意,与指向方法的指针类似,你不能直接使用指向成员的指针,你必须再次提供一个类实例。

我所说的将是有序的证明,所以这是:

class C
{
public:
    int a;
    int b;
};

// Disassembly of fragment of code:

    int C::*pointerToA = &C::a;
00DB438C  mov         dword ptr [pointerToA],0  
    int C::*pointerToB = &C::b;
00DB4393  mov         dword ptr [pointerToB],4  

你能看到存储在pointerToA和pointerToB中的值吗?字段a与类实例的开头相距0字节,因此值0存储在pointerToA中。另一方面,字段b存储在字段a之后,字段长度为4个字节,因此值4存储在pointerToB中。