对象数组与新对象

Array of objects vs new

本文关键字:对象 新对象 数组      更新时间:2024-09-27
#include <iostream>
class A {
public:
A(int d) : y(d) {}
int y;
};
int main(void) {
A d[3] = {A(0), A(1), A(2)};
std::cout << d[1].y << std::endl;
};

我正在为大学做一个项目。我被指示而不是使用对象数组,并用我类的临时对象实例化每个元素——所以类似上面代码的东西是否定的。相反,建议我们使用A*类型的数组,然后在每个元素上使用new,这样我们就可以创建一个对象,并使每个元素指向它。

所以我们会有这样的东西:

A* d[3];
for (int i=0;i<3;i++)
d[i]=new A(i);

然而,与第一个版本相比,我不太理解实践中的差异。据我所知,在第一个版本中,我们使用构造函数创建了三个临时对象(它们是右值(,然后将它们分配给数组。赋值结束后,临时对象将被销毁,其值将复制到数组中。在第二个版本中,我们的优点是可以删除d[]指向的内容,但忽略这一点,使用第一种方法的缺点是什么?

编辑:

class B{
public:
A a1[3];
}

假设我的初始定义后面跟着这个(A不是默认的可构造的,这样的东西是不可能的吗?所以在这种情况下,我不能使用对象数组,而是必须使用类型为A*的指针数组,然后使用new,作为我唯一的手段。

指针数组->创建对象并将其分配给指针时的内存分配。1000个指针的数组,其中有1个元素=内存的1倍。

对象数组->创建数组时的内存分配。内部有1个元素的1000数组=1000倍内存。

1(A d[3],它创建了一个内置数组(或C样式、纯数组、裸数组…(,其中包含(三个(A类型的元素,这些元素存在于堆栈中(根据您的代码(。

2(A* d[3];,它创建了一个内置数组,该数组包含指向类型为A的元素(或者它们应该是(的(三个(指针。这并不意味着指向的元素是A类型的,创建这些元素是开发人员的任务,您可以通过两种方式来完成,使用堆栈(如前所述(:

A element;
...
d[0] = &element;

或者使用堆(具有new/new[]delete/delete[](:

d[0] = new A;
...
delete d[0];

第二个选项的副作用;您可能指向一个已经损坏的元素(即元素超出范围(,因此可能存在seg故障;您可能会丢失指向已分配内存的指针(即指针数组超出范围,并且已分配内存尚未释放(,从而导致内存泄漏。

然而,一个优点是你可以使用惰性初始化(你不需要为你不使用的东西付费(。如果您不需要d[2]中的A对象(或由指向(,为什么要创建它并浪费内存?在这里出现了智能指针,它们让你的生活更轻松,只允许你在需要的时候分配内存,降低了seg故障(值得一提的是std::weak_ptr(、内存泄漏等的风险:

std::unique_ptr<A> d[3];
d[0] = std::make_unique<A>(); // Call make_unique when you need it

但仍然有点令人讨厌,内置阵列很好,但在某些情况下可能会更好。添加或删除元素、迭代、获取大小、访问尾部/头部等都很困难。这就是为什么容器非常有用的原因,即std::arraystd::vector(在编译时不必知道其大小(:

std::vector<std::unique_ptr< A> > d(3);
std::array<std::unique_ptr<A,3> > d;

C++中的以下代码没有任何优势:

A* d[3];
for (int i=0;i<3;i++)
d[i]=new A(i);

事实上,你根本不应该写它。

使用本地声明的临时数组的解决方案是有效的,但有一个显著的限制,即要求数组中的元素数量为编译时间常数(即提前已知(。

如果您需要一个只有在运行时才知道的具有动态元素数的数组,那么您应该使用std::vector,它会自动执行new分配。这是C++中处理动态分配数组的惯用方法。