【问题标题】:Heap vs Stack allocation堆与堆栈分配
【发布时间】:2011-10-06 12:25:57
【问题描述】:

我对在堆上分配对象与在堆栈上分配以及何时以及如何调用 delete() 的主题有点困惑。

例如,我有类 Vector。我想做一个数组。

我可以这样做

Vector** v = new Vector*[100]; //create an array of 100 pointers to 100 Vector objects

据我了解,这会将所有内容(除了指针地址)分配在堆上吗? 所以要释放内存,我需要:

for (int i = 0; i < 100; ++i)
{
   delete(v[i]);
}
delete(v);

或者只是

delete(v);

够了吗?

现在再举一个例子:

Vector* v = Vector[100];

在这种情况下发生了什么?分配发生在哪里?堆还是栈? 我还需要打电话吗

delete(v);

但这还不是全部问题,抱歉发了这么长的帖子..

示例:

class Vector
{
  int x, y, z;
}

Vector* v = new Vector();

x、y、z 分配在哪里?堆还是栈?

或者这个怎​​么样:

class Vector2
{
   int items[10];
}

Vector2* v2 = new Vector2();

items[10] 分配在哪里? 如何删除 v2?我需要自定义析构函数吗?

最后但并非最不重要的是这个怎么样:

class Vector3
{
   int* items;
}

Vector3 v3 = Vector3();

项目指针存储在哪里?堆还是栈?如何删除?

感谢和抱歉很长的问题。这东西困扰了很久,网上也找不到完整的解释。

【问题讨论】:

  • 忘记新/删除你不需要它们。只需使用 std::vector
  • 对不起老兄,这只是我为了理解内存分配而提供的一个示例类。这与实际应用没有任何关系。我不妨称它为 MyClass。 + 即使它是 std::Vector 也是别的东西,不要混淆名称!
  • 跟名字无关。更多的是使用new X[]。在大多数情况下,您应该使用std::vector&lt;X&gt;

标签: c++


【解决方案1】:

我会从头开始...

Vector** v = new Vector*[100];

在堆上分配一个包含 100 个指针的数组,指向 Vector 类型的对象 它返回一个指针 - v - 你可以使用它来跟踪这个指针数组。

删除这个包含 100 个点的数组:

delete[] v;

(使用 delete 运算符 - delete 用于单个分配的对象,delete[] 用于数组)

下一个案例(我假设您的意思是new Vector[100]

Vector* v = new Vector[100];

您在堆上分配了一个包含 100 个向量的数组,并获得了一个指向其起始位置的指针 - v。 删除这个数组:

delete[] v;

下一步...

class Vector
{
  int x, y, z;
}

Vector* v = new Vector();

这会在堆上分配一个 Vector 类的对象,并给你一个指针来跟踪它。因为你在堆上分配了整个对象,所以 x、y 和 z 都在堆上分配。

删除它:

delete v;


class Vector2
{
   int items[10];
}

Vector2* v2 = new Vector2();

这个有点棘手,但我会推理出来......

类是蓝图。在您以某种方式实例化类之前,您根本没有分配任何内存,在这种情况下是在堆上。因为该类是一个蓝图,所以在您在堆上创建类 Vector2 的对象之前,无法分配 items。我认为我们可以合理地推断出items 因此是在堆上分配的。

删除 v2:

delete v2;

最后:

class Vector3
{
   int* items;
}

Vector3 v3 = Vector3();

您在堆栈上分配了所有类 Vector3,其中的指针items 也因此分配。堆上什么都没有,所以不要删除它。

【讨论】:

  • 我认为一个简单的delete[] v; 可能会导致一些内存泄漏(如果分配的指针最终被使用)。
  • 忘记模板。在您开始考虑模板之前,您需要正确理解动态内存。模板是一门完整的子语言(您需要先了解主要语言)。
  • @Martin:我不想在模板的实际 C++ 意义中给出模板的印象(vector 等)。我试图传达这样一种理解,即在进行类声明时,它可以作为该类未来分配的蓝图。另外,我认为 Naumov 是在 X、Y、Z 值的意义上使用 Vector(与 3D 坐标平面的思想一致),而不是在 STL 模板的意义上。它们是两种不同的东西。
  • @Muggen:他第一次调用时只分配了一个包含 100 个指针的数组。如果他真的继续对这些指针做一些事情,例如用它们分配 100 个 Vector 对象,只会有内存泄漏。他没有,所以一个简单的 delete[] 就足够了。
  • @dragonwrenn:在谈论 C++ 时重载 tempate 这个词是个坏主意。也改变你的短语:Classes are a blueprints.
【解决方案2】:

让我们开始吧,您可能不需要动态分配任何东西。
静态数组或向量都会更好。

但是让我们假设您这样做是为了学习。

1 分配指针数组

Vector** v = new Vector*[100]; //create an array of 100 pointers to 100 Vector objects
                               //
                               // The above comment is misleading.
                               // The 100 pointers have not been initialized.

这分配了一个内存区域,该区域有 100 个指针空间(指向 Vector(注意它们是未初始化的,即每个指针都是随机的))。要删除它,您需要执行以下操作:

delete [] v;

2 分配数组的成员

如果你分配每个成员(如果你想使用它们,你应该这样做:

for (int i = 0; i < 100; ++i)
{
   v[i] = new Vector;
}

// Code

for (int i = 0; i < 100; ++i)
{
   delete v[i];
}

所以请注意,每次调用 new 都应该有一个对应的 delete 调用。

3 一个错误

Vector* v = Vector[100];

这是错误的。如果不会编译。

4 会员去哪儿

除非成员是指针,否则它位于对象内部。
如果成员是指针,则必须单独分配。

class Vector
{
  int x, y, z;
}

Vector* v1 = new Vector();
Vector  v2 = Vector();      // Yes the = Vector() is required

这里 v1 指向一个动态分配的对象,其中包含 x/y/z
这里 v2 是一个包含 x/y/z 的对象

我知道人们会说= Vector(); 是不需要的或复制结构。两者都是正确的,但都没有抓住重点。 1)它是一个复制结构,但编译器总是足够聪明地删除它。 2)需要使其与上面的行等效。区别在于没有它是default-initialized(即未初始化),它的成员是zero-initialized(因为Vector()只有一个编译器生成的构造函数)。

那么数组成员呢。
他们与其他成员没有什么不同。成员总是在对象内部分配。如果成员是指针,则它在对象内部,但必须明确设置它指向的内容。

class Bob
{
    int   dataArray[10];
    int*   dataPtr;
};
Bob  b1 = Bob();
Bob* b2 = new Bob();

b1.dataArray[0] = 1;            // dataArray is inside the object.
                                // b1 is allocated locally

b1.dataPtr      = new int [10]; // dataPtr is inside the object.
                                // But what it points at must be seprotally defined.
                                // Note you must call delete [] for each new []
b1.dataPtr[5] = 2;

b2->dataArray[0] = 1;           // dataArray is inside the object.
                                // b2 is allocated dynamically

b2->dataPtr      = new int [10];// dataPtr is inside the object.
                                // But what it points at must be aseptically defined.
                                // Note you must call delete [] for each new []
b2->dataPtr[5] = 2;

【讨论】:

    【解决方案3】:
    1. delete [] v; - 它是删除运算符的数组表示法。删除数组时必须使用它,而不是顺序删除每个元素。
    2. Vector* v = Vector[100]; 只是不会编译。写入Vector v[100];,它将在堆栈上分配向量数组。您不能手动删除它。
    3. x, y, z 在堆上,因为整个对象都在堆上
    4. items[10] 也分配在堆上,因为它是对象的一部分。要删除它,只需致电delete v2;。除非您在构造函数中分配任何内容,否则您不需要特殊的析构函数。
    5. int* items; 存储在堆栈中,因为它是在堆栈上分配的对象的一部分。您不必删除它,它会在超出范围时自动删除。

    【讨论】:

      【解决方案4】:

      一般规则:

      • 当您使用newnew [] 时,您在堆上分配,在其他情况下您在堆栈上分配。

      • 每次使用 new 时都应该使用 delete(无论是否明确)

      • 每次使用 new[] 时都应该使用 delete[](无论是否明确)

      下一个代码是 UB,因为您使用了一个 new[] 和 101 delete。使用一个delete[]

      Vector** v = new Vector*[100];
      for (int i = 0; i < 100; ++i)
      {
         delete(v[i]);
      }
      delete(v);
      

      【讨论】:

      • 未定义的行为,语言标准未完全指定的情况,因此每种语言实现都可以按照自己的意愿行事。它可能会崩溃、损坏您的数据或发射航天飞机。
      猜你喜欢
      • 1970-01-01
      • 2011-05-28
      • 2011-09-06
      • 2013-10-20
      • 2010-10-03
      • 2012-06-26
      • 2014-06-23
      • 2023-03-03
      相关资源
      最近更新 更多