5

考虑以下一段 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 ?

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

4

6 回答 6

5

简单地声明

Object array_of_objects[10];

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

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

编辑:链接到 StackOverflow 上的其他问题 How to useplacement new for the array 在 StackOverflow 上对这个问题的回答中进行了解释。

于 2008-11-26T12:20:42.537 回答
4

在 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
于 2008-11-26T12:21:27.730 回答
4

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

静态分配是老生常谈

int myarray[10];

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

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

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

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

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

于 2008-11-26T12:38:06.560 回答
3

对于 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,那么你需要一个指针容器。为了获得良好的资源处理,这可能意味着您需要一个智能指针容器。

于 2008-11-26T12:22:05.297 回答
2

您甚至可以在堆栈上分配可变数量的对象。您必须混合使用 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])();

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

于 2008-11-26T12:28:04.567 回答
0

如果你碰巧使用 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 的堆分配是你程序的瓶颈之一时才使用它。

于 2013-12-03T19:09:03.173 回答