在堆栈和堆上创建对象数组

26

考虑下面的代码:

class myarray
{
    int i;

    public:
            myarray(int a) : i(a){ }

}

如何在堆栈上创建一个myarray对象的对象数组,以及如何在堆上创建一个对象数组?


这是一道作业题吗?听起来像是。 - Amber
2
不是作业问题...这是我在为面试做准备时在网上发现的.... :) - Light_handle
6个回答

61

您可以通过以下方式在堆栈上创建对象数组

myarray stackArray[100]; // 100 objects

在堆上(或“自由存储区”):

myarray* heapArray = new myarray[100];
delete [] heapArray; // when you're done

但最好不要自己管理内存。相反,使用std::vector

#include <vector>
std::vector<myarray> bestArray(100);

向量是动态数组,默认情况下从堆中分配元素。††


因为您的类没有默认构造函数,所以要在栈上创建它,您需要让编译器知道要传递什么给构造函数:

myarray stackArray[3] = { 1, 2, 3 };

或者使用向量:

// C++11:
std::vector<myarray> bestArray{ 1, 2, 3 };

// C++03:
std::vector<myarray> bestArray;
bestArray.push_back(myarray(1));
bestArray.push_back(myarray(2));
bestArray.push_back(myarray(3));

当然,你也可以为它提供一个默认构造函数:

class myarray
{
    int i;    
public:
    myarray(int a = 0) :
    i(a)
    {}
};

† 对于学究型人士:C++并没有真正的"stack"或是"heap"/"freestore"。我们所拥有的是"automatic storage"和"dynamic storage" duration。实际上,这会与栈内存分配和堆内存分配对齐。

†† 如果您希望从栈中进行"dynamic"内存分配,您需要定义一个最大大小(栈空间提前知道),然后给向量一个新的分配器,以便它使用堆栈。


@GMan - 这是一个非标准但广泛提供的C函数。 - Chris Lutz
2
它在C++中的工作方式与在C中相同;如果有更标准的方法告诉编译器在堆栈上分配N个字节,其中N是在运行时确定的,我不知道是什么。 - Crashworks
只有一点小细节,我不确定 vector 是否指定元素将在堆上。例如经典的 string 实现将字符保存在对象本身内,通常最多为8或16个字符。然后当需要延长时,它切换到堆上。因此,如果 string 对象本身位于栈上,则所有长度小于16的字符串的数据也将位于栈上。人们可以想象 vector 在实现中也可能表现出相同的行为,是吗? - v.oddou
@v.oddou 为什么 C++ 的创造者要在堆上分配向量内存?我们知道向量在内存中也是连续的,如果向量的使用者超过了向量的初始容量,就需要重新分配内存。这也可以在堆栈上完成。或者我有什么遗漏吗? - Light71192
@Light71192 标准关于向量的唯一规定是“支持随机访问迭代器。此外,它支持(摊销)常数时间插入和删除操作在末尾;在中间插入和删除需要线性时间。存储管理是自动处理的,但可以给出提示以提高效率。向量的元素是连续存储的。”我认为这意味着数据存储位置取决于分配器。可能是堆栈,但O(move)将是O(copy)(我不知道复杂度是否是分配器的限制)。 - v.oddou
显示剩余2条评论

7

自从C++11以来,std::array<T,size>可用于在堆栈上分配的数组。它包装了T[size],提供了std::vector的接口,但大多数方法都是constexpr。这里的缺点是您永远不知道何时会溢出堆栈。

std::array<myarray, 3> stack_array; // Size must be declared explicitly.VLAs

对于使用堆内存分配的数组,请使用std::vector<T>。除非您指定自定义分配器,否则标准实现将使用堆内存来分配数组成员。

std::vector<myarray> heap_array (3); // Size is optional.

请注意,在这两种情况下都需要一个默认构造函数来初始化数组,因此您必须定义它。

myarray::myarray() { ... }

还有使用C的VLA或者C++的new的选项,但是应该尽可能避免使用它们,因为它们的使用容易导致代码出现分段错误和内存泄漏。


std::array 很好,因为 - 像任何其他良好编程的 T[n] 包装器一样 - 它知道自己的大小(通过模板魔法),可以以更令人愉悦的方式传递,可以从函数中返回等等。我确实喜欢“你永远不知道何时会溢出堆栈” - 好吧,除非它导致非堆栈内存的完全随机损坏并使其非常明显 :-) 但是,当然,最好避免在堆栈上分配大型数组。 - underscore_d

2

如果您在堆栈或堆上创建一个myarray类的对象数组,您需要定义一个默认构造函数。

创建对象数组时无法传递参数给构造函数。


0

我知道如何在默认构造函数之外创建对象,但只能在堆栈上:

假设您想要为MyArray类创建10个对象,其中a = 1..10

MyArray objArray[] = { MyArray[1], MyArray[2]......MyArray[10]}

不需要调用析构函数,因为它们是在堆栈中创建的。

1
语法?使用圆括号,以下使用临时变量: MyArray objArray[] = { MyArray(0), MyArray(88), 等等,} - Hoven
如前所述,这段代码无法编译。 - underscore_d

0

如果要使用分配了最大大小的堆栈内存,并通过运行时选择的大小查看它,可以使用std::span。

#include <span>
using namespace std;

struct Toto{ int i; char name[20];};
Toto mem[100];
int n=3;
std::span<Toto> s(&mem[0], n);

我的使用情况涉及一个被调用了一百万次的函数,并使用了几个数组(目前是std::vectors)。我想知道是否可以通过这种方式替换std::vector来提高速度。还没有尝试过...另一个可能性是只分配一次std::vector而不是一百万次。


-1
#include <stdio.h>
class A
{
public:
  A(int a){ 
       printf("\nConstructor Called : %d\n",a); 
       aM = a;
      }  
  ~A(){ 
    printf("\ndestructor Called : %d\n",aM);
}
private:
  int aM;
};

int main()
{                                                                                                   
  A **a = new A*[10];
  for (int i = 0;i<10;i++)
    a[i] = new A(i+1);
    for (int i = 0;i<10;i++)
      delete a[i];// = new A(i+1);                                                                                    

  delete []a;
}

我也看不到哪里有A的任何数组,除非对象被视为大小为1的数组。 - Peter - Reinstate Monica

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接