C++ 对象表示

标签 c++ oop class object memory-management

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

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

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

int (MyClass::*var);

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

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

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

我不确定我是否正确解释了我的疑问,如果没有,请告诉我,我会尽力更好地解释

最佳答案

类在内存中的存储方式非常简单——几乎与结构相同。如果您检查存储类实例的内存,您会发现它的字段只是一个接一个地打包。

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

关于C++ 对象表示,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15186192/

相关文章:

java - 如何使用 for 循环更新数据库并获取最后一个序列值

c++ - g++ 不会在模板代码中发出 -Wsign-compare

c++ - 如何为 Qt Quick Designer 模拟 C++ 枚举?

c++ - double to string 没有科学记数法或尾随零,有效

python - 在这个Python类中,如何将值赋给 "other"变量?

java - Android - 使用另一个类的方法不起作用

c++ - 如何将多个 directx LP3DXMESH 组合成 1 个网格以实现快速渲染?

javascript - 我如何命名伪经典javascript

c++ - 通过引用传递父对象

python - 如何在不创建实例的情况下选择性地访问类方法