C++:如何在堆栈上创建对象数组?

Posted

技术标签:

【中文标题】C++:如何在堆栈上创建对象数组?【英文标题】:C++: how to create an array of objects on the stack? 【发布时间】:2010-09-24 03:29:48 【问题描述】:

考虑以下一段 Java 代码。

int N = 10;
Object obj[] = new Object[N];
for (int i = 0; i < N; i++) 
    int capacity = 1000 * i;
    obj[i] = new ArrayList(capacity);

因为在 Java 中,所有对象都存在于堆上,数组并不存在 包含对象本身,但对对象的引用。还, 数组本身也是一个对象,因此它存在于堆中。

什么是 C++ 中的等价物,但保持数组和对象打开 堆栈,尽可能避免需要 new 和 delete ?

编辑:更改代码以使用自定义构造函数。

【问题讨论】:

【参考方案1】:

简单地声明

Object array_of_objects[10];

在 C++ 中,在堆栈上创建 10 个默认构造的 Object 类型的对象。

如果您想使用非默认构造函数,这在 C++ 中并不容易。可能有一种新的放置方式,但我无法告诉你。

编辑:链接到 *** 上的其他问题 *** 上对 this question 的回答中解释了如何为数组使用新位置。

【讨论】:

没有直接的办法;您可以使用以下答案之一中提到的 std::vector。【参考方案2】:

在 C++ 中,堆栈上不可能有一个大小在运行时确定的数组。在这里你使用 std::vector 来做到这一点:

int N = 10;
std::vector<Object> obj(N);
// non-default ctor: std::vector<Object> obj(N, Object(a1, a2));
// now they are all initialized and ready to be used

如果在编译时知道大小,您可以继续使用普通数组:

int const N = 10;
Object obj[N];
// non-default ctor: Object obj[N] = 
//      Object(a1, a2), Object(a2, a3), ... (up to N times) ;
// now they are all initialized and ready to be used

如果允许你使用 boost,最好使用 boost::array ,因为它提供了像容器一样的迭代器,你可以使用 .size() 来获取它的大小:

int const N = 10;
boost::array<Object, N> obj;
// non-default ctor: boost::array<Object, N> obj = 
//       Object(a1, a2), Object(a2, a3), ... (up to N times)  ;
// now they are all initialized and ready to be used

【讨论】:

当然,在 std::vector 的情况下,“数组”在堆栈中,但对象不在。 如果您使用标准分配器,“数组”和对象实际上都不在堆栈上。 实际上,一些编译器(尤其是 g++)确实支持具有运行时确定大小的堆栈数组。这显然是一个移植的 C99 特性,称为可变长度数组。 是的,你是对的。 gcc 支持 c99。但 C++ 不需要它:)【参考方案3】:

可以“静态”(在编译时知道大小)或“动态”(在运行时确定大小)进行分配。

静态分配是老生常谈

int myarray[10];

要在堆栈上分配,您需要alloca 分配函数,它本质上只是增加堆栈指针。 (或递减......无论如何)。释放是自动完成的。

int* myarray = (int*) alloca( n*sizeof(int) );

所以你可以像Nils 显示的那样在堆栈上初始化一个数组。

std::vector 可以在堆栈上工作,如果提供堆栈分配器(vector 的第二个,繁琐的模板参数)

我的猜测是 Boost 就是这样做的。

【讨论】:

【参考方案4】:

对于 ArrayList 对象的数组:

ArrayList obj[10];

对象将被默认初始化,这对于用户定义的类型很好,但可能不是您想要的内置类型。

同时考虑:

std::vector<ArrayList> obj(10, ArrayList());

这通过复制您作为第二个参数传递的任何内容来初始化对象。所以它们都是一样的,但不一定是默认的。正如 litb 所指出的,向量中的“10”可以被非常量表达式替换,而数组声明中的“10”则不能。

这实际上并没有将 ArrayList 对象放入堆栈,而是将所有 10 个对象放入堆中的单个分配中。因此,如果您真的负担不起单个分配,则可能很少会出现性能问题。但是,std::vector 在堆栈上,它会在销毁时删除它使用的所有堆对象。因此,为了确保您的资源被释放,向量的行为“好像”它都在堆栈上。

请注意,将 Object 容器与 ArrayList 值混合在一起,就像您在示例 Java 代码中所做的那样,在 C++ 中充满危险。基本上你做不到,即使 ArrayList 扩展了 Object,因为数组只包含 10 个对象的存储空间,而 ArrayList 可能需要比 Object 更多的字节来存储。结果是您尝试复制到数组中的任何 ArrayList 都会被“切片”:仅将其表示的初始部分放入数组中。

如果你想要一个类型的容器,说它包含 Objects,但实际上包含 ArrayLists,那么你需要一个指针容器。为了获得良好的资源处理,这可能意味着您需要一个智能指针容器。

【讨论】:

这不是用向量中所有条目共享的单个 ArrayList 来初始化向量吗? 确实,这不会在堆栈上分配 - 除非为向量提供了另一个分配器。 是的,这就是为什么我将向量作为替代方案来考虑,而不是作为问题的直接答案。毕竟,这不是一个测验,因此提供与问题实际内容相似的方法通常很有用,希望它符合实际要求。【参考方案5】:

您甚至可以在堆栈上分配可变数量的对象。不过,您必须混合使用 C 和 C++。

// allocate storage for N objects on the stack
// you may have to call _alloca and include something to use this.
object * data = (object *) alloca (N * sizeof (object));

// initialize via placement new.
for (int i=0; i<N; i++)
  new (&data[i])();

代码未经测试,但原则上它是这样工作的。

【讨论】:

我强烈反对这样做。您在上面使用的 ode 已经通过 std::vector 做得更好(使用堆而不是不可移植的 alloca)。 我想说这是值得了解的,因为在这种非常罕见的情况下,堆太慢并且你知道 N 的上限和你自己的堆栈使用情况,这样数组就可以适应了。无论如何都不是首选解决方案,但它是我们所要求的...... 很公平 :) 但是他询问了一个 c++ 解决方案。 alloca 甚至不是 POSIX :) 在我作为开发人员的 10 年中,我曾经使用过一次placement new - 那里有一个我无法触及的静态分配数组(遗留库代码),但我必须在其中获取一个对象没有默认构造函数。讨厌的黑客;-) 我必须同意,伙计们。我也从不在生产代码中做这样的黑客攻击。【参考方案6】:

如果你碰巧使用Qt,可以使用QVarLengthArray

它将大小作为第二个模板参数,它将静态分配具有该大小的数组,并将其用作数组的支持,而不是像 std::vector 或 QVector 那样的堆。如果您添加的大小超过了模板指定的大小,它将改用堆分配。

例子:

//the following ints will all be stored on the stack,
//and a heap allocation is never performed to store the array
QVarLengthArray<int, 10> objArray;
for (int i = 0; i < 8; i++) 
    int capacity = 1000 * i;
    objArray.push_back(capacity);


//since it's a class and not a raw array, we can get the array's size
std::cout << objArray.size(); //result is 8

//a heap allocation will be performed if we add an eleventh item,
//since the template parameter of 10 says to only statically allocate 10 items
objArray.push_back(0); //9 items
objArray.push_back(0); //10 items
objArray.push_back(0); //11 items - heap allocation is performed

如果您保持在模板参数大小以下,您将避免堆分配对性能的影响 - 您将有效地拥有一个动态分配的基于堆栈的数组。唯一的缺点是,如果您不使用模板参数指定的那么多项目,则会浪费内存:如果您使用的项目太少,则会浪费空间。如果你使用太多,那么整个堆栈分配区域都被浪费了。

有时用性能换取内存是值得的,有时则不然。我建议不要盲目地使用这个类——只有当你知道通过分析 std::vector 的堆分配是你程序的瓶颈之一时才使用它。

【讨论】:

以上是关于C++:如何在堆栈上创建对象数组?的主要内容,如果未能解决你的问题,请参考以下文章

使用 ASM 监视 Java 中的对象创建时出现“VerifyError:期望在堆栈上找到对象/数组”?

大数组大小的分段错误

如何创建从 C++ 对象数组填充的 qtablewidget?

如何在 C++ 中构建一个对象数组,这些对象都继承自一个主类?

在c++中如何调用数组对象的构造函数

在 C++ 中使用指向数组中对象的指针会更好吗?