内存可分为4个区域。
第一个是堆栈,第二个是堆,第三个是代码,最后一个是数据。
我想知道类和类的方法存储在哪里。
你能解释一下吗?
警告:理论上,这个答案的部分内容可能会有很大差异(尽管事实上,至少在大多数典型/常见的实现中存在相当程度的一致性)。因此,此答案的部分内容主要基于您通常可以预期的内容,即使可能会有更多变化。
“方法”(成员函数)是代码,位于其他代码中。与其他函数的主要区别在于,成员函数通常期望接收一个隐藏参数(称为this
),该参数为其提供任何对象数据的地址(间接包括类的 vtable 指针)。
类数据的位置通常取决于类对象的分配方式,就像任何其他变量一样:全局或静态局部对象将被静态分配(在您的列表之外,显然是“数据”);一个本地自动变量将被自动分配(又名“在堆栈上”),一个动态分配的变量将被动态分配(大惊喜)(又名“在堆上”)。
当然,可以稍微改变这些规则——例如,一个static
成员变量将被静态分配,而不管对象的其余部分是如何分配的。
方法不占用实例存储。OOP 创造了某个方法是类成员对象的错觉,但这只是一种范式,实际上您可以将成员方法视为仅在它们“关联”的对象范围内可见的全局函数。
简而言之,C++ 成员方法SomeType.someMethod()
与 CSomeType
结构和伴随的 相同SomeType_someMethod(SomeType* self)
,这是 C++ 编译器自动生成的。
类中唯一实际占用实例数据空间的部分是非静态成员类型。所以成员方法在逻辑上占据了内存的“代码”和“数据”部分,因为一个方法就是这样——一个指令列表以及对内存中全局和局部对象的引用。
由于 C++ 允许递归和所有其他“动态特性”,函数的每个实例都实例化其自己的本地对象,因此非静态和非 const 本地自动成员从全局内存复制到堆栈,或者以立即值或对全局静态数据的引用。这仅适用于非常量本地成员数据,a 方法不会更改其指令,因此与本地不同,只需要方法代码的一个实例。
所以,总结一下,成员方法,很像静态成员对象不占用实例空间,但是,它们在调用时可能会占用堆栈空间,甚至在递归情况下可能导致堆栈溢出。
编辑:还可能值得注意的是,在虚拟方法的情况下,这将占用每个对象的一个 void 指针空间来保存虚拟表。此外,大多数多重继承实现通常会使用多个 vtable。
类的定义存储在程序的代码部分中(如果您考虑一下,那将是有道理的,因为该定义实际上是一组函数,它们在概念上将对象作为名为“this”的附加参数)。但是,虚拟类的每个实例都需要包含指向其虚拟表的指针(以便能够执行动态调度),并且该指针将驻留在恰好分配该实例的任何位置(对于虚拟情况,通常在堆上)。
那些语言律师的坚持者会指出,C++ 标准并没有规定诸如堆和堆栈之类的东西。我很清楚这一点,但出于所有实际目的,这就是它的工作方式。