堆上的类的成员变量存储在哪里?

时间:2011-10-02 12:37:06

标签: c++

当一个类在堆上实例化时。是否所有成员变量也分配在堆上或其他地方。那么在堆上显式分配成员变量有什么好处吗?

struct abc {
std::vector<int> vec;
}


int main() {
abc* ptr = new abc(); //the "class" is on the heap but where is vec?
ptr->vec.push_back(42); //where will the 42 be stored?
return 0;
}

这会有什么不同

struct abc {
std::vector<int>* vec_ptr;
abc() { vec_ptr = nev std::vector<int>(); }

int main() {
abc* ptr = new abc();
ptr->vec->push_back(42);
}

3 个答案:

答案 0 :(得分:4)

非静态数据成员存储在它们所属的对象(类实例)中。

如果您将对象创建为具有自动存储持续时间的本地对象,则其成员为 在里面。如果动态分配对象,它们就在其中。 如果使用一些完全不同的分配方案分配对象,则其成员仍将位于其中的任何位置。对象的成员是该对象的部分

请注意,此处的vector实例位于您的结构中,但vector本身为您推送到其中的项目管理自己的动态存储空间。因此,abc实例在通常的免费商店中动态分配,其中包含vector成员,42位于由vector实例管理的单独动态分配中。

例如,说vector是这样实现的:

template <typename T, typename Allocator = std::allocator<T>>
class vector {
    T *data_ = nullptr;
    size_t capacity_ = 0;
    size_t used_ = 0;
    // ...
};

然后capacity_used_都是矢量对象的一部分。 data_ 指针也是对象的一部分,但由向量管理的内存(指向data_)不是。


关于术语的说明:

  • 具有自动存储持续时间最初是在堆栈上。正如Loki指出的那样(和我mentioned myself elsewhere),自动本地存储通常使用调用堆栈实现,但它是一个实现细节。
  • 动态最初是在堆上 - 同样的异议适用。
  • 当我说通常的免费商店时,我只是指::operator new 管理的资源。那里可能有任何东西,这是另一个实现细节。

答案 1 :(得分:0)

您的问题可以简化。考虑:

int main()
{
  std::vector<int> v;
  v.push_back(42);
}

42存储在哪里?”

在此示例中,v是一个自动变量,因此它存储在本地作用域的存储(通常是堆栈)中。但请注意v具有固定的小尺寸 - 它只是处理程序对象。包含元素的实际内存由向量的内部逻辑单独分配,使用指定的分配器(在本例中为std::allocator),后者从免费存储中获取所有内存。

类的所有(非静态)数据成员构成该类的数据类型的一部分并占用空间 - 这基本上与哑C struct相同。但是,大多数C ++库类的神奇之处在于它们的数据成员只是微小的指针,并且所有有效负载数据都是单独分配的,并由类的内部逻辑进行管理。

因此,当你创建一个包含大量矢量成员的类时,类本身仍然不会很大。该类的任何实例都将在任何地方(无论是自动,动态,静态)完整地分配,但要注意每个成员对象将单独请求额外的单独内存来完成其工作。

答案 2 :(得分:0)

new ed对象视为连续分配。创建abc的新实例会创建最小大小为sizeof(abc)的分配。

当您通过new创建时,系统不会单独分配该类的成员。举例说明:new不会为每个成员,子成员调用new。因此,数据成员存储在由new创建的连续分配中,这是一个单独的分配。

vector的情况下,向量内部对其元素使用动态分配 - 这是使用单独的分配完成的。这个分配是在vector的实现中完成的。

因此成员确实驻留在堆上,但它们作为abc实例的一部分存在,并且该实例使用一个分配,不包括数据成员创建的任何分配。

  

那么在堆上显式分配成员变量有什么好处吗?

是肯定的。您可能选择这样做的原因有很多。考虑问题的背景:你应该赞成将你的成员声明为值,直到有充分理由不(details here)。当你需要将你的成员声明为堆分配时,总是使用适当的容器(例如自动指针,共享指针),因为这样可以节省大量的精力,时间和复杂性。