【问题标题】:Allocate memory for 2D array with C++ new[]使用 C++ new[] 为二维数组分配内存
【发布时间】:2012-12-25 21:27:30
【问题描述】:

当我从用户那里读取一些值并且我需要创建一个特定大小的数组时,我会这样做:

#include <iostream>
using namespace std;    
unsigned* numbers;
int main()
{
int a;
cin >> a;
numbers = new unsigned[a];
}

如何使用二维数组(从用户读取的大小为 a*b)?

【问题讨论】:

  • 但是为什么呢? whyyy?(提示:std::vector)同样,二维数组是邪恶的。
  • @BartekBanachewicz ,您的评论对于初学者来说是没有建设性的。 gen ,向量的向量是可能的,但有相关的性能损失(我不会费心进入这里)
  • 怎么样,@brennan?提出唯一合理的解决方案怎么会没有建设性?
  • @BartekBanachewicz 因为认为无论他在哪里学习 C++ 都值得在进入标准库之前教他核心语言(什么是数组和指针),这并非不合理。您可能同意也可能不同意的教学策略,但肯定是合法的。简单地告诉他他的“提示”是错误的,这对于没有看过 C++ 标准库的人来说似乎非常神秘是没有帮助的。就像说某事是“邪恶的”,没有任何解释。
  • @Brennan 错了,错了,错了。谁点赞了这条评论?标准库核心语言,远比 C 风格的数组和指针更基础,尤其是在 C++11 中(甚至在此之前)。随着教学策略的发展,你的教学策略很糟糕而且适得其反。 Bartek 的建议很中肯。

标签: c++ arrays memory-management multidimensional-array


【解决方案1】:

如果a和b分别是行数和列数,你可以这样分配数组:

new unsigned[a * b];

要访问i 行和j 列的元素,请执行以下操作:

numbers[i * b + j]

但是,请注意,实际上,无论您尝试做什么,使用std::vector 几乎肯定会更好,但您可能还没有了解这一点:)

【讨论】:

  • 我对向量有点了解,关于它们有什么“棘手”的事情我应该知道吗?
  • 您可能对向量上的reserve(n) 函数感兴趣。它确保向量有足够的内存分配来保存你试图在其中存储的任何内容。编辑:这不是正式必要的。如果您不调用它,向量将处理重新分配新内存并将其内容复制到新内存时,只要内存不足。所以无论有没有reserve,行为都是一样的。但出于性能原因,您可能需要致电 reserve 以避免所有这些重新复制。
【解决方案2】:

任何在代码中看起来像二维数组的东西都不会是内存中的物理二维数组,而是一块普通的内存块或分散的内存块,具体取决于您如何分配它。

  • 您可以通过在另一个包含 N 个指针的动态数组中动态分配 N 个数组来折磨自己,就像 nrussel 的回答所暗示的那样。
  • 您可以改为创建一个向量向量,就像 billz 和 Arun C.B 建议的那样 - 这样可以减轻您自己管理分配的内存的负担,但仍然会给您留下 N+1 个分散的分配,这不是很好的性能。
  • Brennan Vincent 的回答建议分配一个动态数组,其中包含 a*b 元素,这会在内存中为您提供一个连续的块。将其与他提到的std::vector 的内置动态内存管理相结合,您会很高兴:

    std::vector&lt;unsigned&gt; matrix(a*b);

如果您希望矩阵便于访问,请将整个事物包装到一个类中,以便您可以访问具有 2D 坐标的元素。但请不要自己管理内存。它只会伤害您和任何必须维护该代码(并搜索内存泄漏)的人。

【讨论】:

  • 这是迄今为止最好的答案,IMO。给出(可以说)正确的答案,解释为什么它(可以说)是正确的,并避免居高临下。
  • tbh,我忽略了你关于 std::vector 的注释,所以这只是你给他的答案,还有一些额外的解释。
  • 嗯,使用答案做出更好的答案是 stackexchange 的灵魂 :) 牛顿所说的站在巨人的肩膀上是什么意思...
【解决方案3】:

可以只用一个向量来表示二维数组:

   typedef std::vector<int>     d1_type;       // 1D
   typedef std::vector<d1_type> d2_type;       // 2D
   typedef std::vector<d2_type> d3_type;       // 3D


   int num = 5;
   d2_type value2d(num, d1_type(num));
   d3_type value3d(num, d2_type(num, d1_type(num)));

你可以像数组一样访问2D/3D向量,例如:

 value2d[0][0] = 100;
 value3d[0][0][0] = 100;

【讨论】:

  • 值得一提的替代构造函数允许构造具有特定尺寸的vector
  • 如果您的数组不是锯齿状的(每一行都有相同数量的条目),那么您不需要这样做,您可以在一个向量中分配所有内容,正如我在回答中提出的那样.然后,您可以一次预订所有需要的空间。
  • 对不起@hmjd,更具体?
  • vector constructors。有一个构造函数允许构造一个包含特定数量元素的vector
  • 如果我希望这个 2d 向量是全局的,那么在将值读入向量时,如下所示:for(i) {numbers.push_back(); for(j) {cin &gt;&gt; a; numbers[i].push_back(a);}} ??
【解决方案4】:

更新建议:

值得注意的是boost already did that. 所以我会坚持使用它,如果它不打算封装在一个类中。

原答案:

一般来说,使用原始数组和分配不如使用std::vector。现在,您当然可以使用vector&lt;vector&lt;T&gt;&gt;,但这不仅打字太长,而且还可能涉及不必要的复制。

使用ptr_vectorvector&lt;unique_ptr&lt;vector&lt;int&gt;&gt; 可以解决第二个问题,但会使问题更加复杂。那么如何解决呢?

很简单:根本不要使用二维数组

想象一下你在内存中的二维数组:

[X|X|X|X]
[Y|Y|Y|Y]
[Z|Z|Z|Z]

很明显,它可以放在一行中:

[X|X|X|X|Y|Y|Y|Y|Z|Z|Z|Z]

现在我们回到旧的、熟悉的一维数组(不管它是 vector 还是 C 样式数组)。为了得到你的元素,你必须知道你的初始数组的宽度,然后找到第n行对应的数组元素,然后简单地添加列数:

int& access (unsigned x, unsigned y) { 
    return data[x * sizeY + y]; // Or (y * sizeX + x)
}

由于是stub方法,实际使用中可能会出现问题。下面是一个全局实现的例子:

int& accessVectorAs2d (std::vector<int> & data, unsigned x, unsigned y, unsigned int sizeY); // inside stays the same
unsigned SizeX = 20, SizeY = 20;
std::vector<int> V (SizeX * SizeY, 0);

accessVectorAs2d(V, 1, 3, SizeX) = 5;

返回引用以允许以与普通二维数组非常相似的方式使用一维结构:

// So you can do either reads
int X = access(1, 3);

// Or writes!
access(1,3) = 5;

附加说明:如果您要构建自己的类,重载operator() 可以为您提供更好的结果:

Data(1, 3) = 5;
int X = Data(1, 3);

现在您将如何实现该访问(子类化或封装向量,或仅使用普通的全局函数)并不重要。

我强烈建议您在此处使用std::vector。它将确保没有内存泄漏(忘记删除),更改大小会更容易(通过.push_back().reserve())并且通常是建议的做法;毕竟,你是用 C++ 编写的,而不是 C。

看!此后的龙!

实际上这个类当然应该是模板化的,不仅有类型,还有维度的数量(好吧,可以简化)和(每1)维度的大小(-1,以允许一侧无限增长)。这可能最好通过在编译时创建适当的访问函数来完成,计算

总和i = 0i (大小i)i * Di,

其中 n 是维数,S 是维数数组,D 是坐标数组。

【讨论】:

  • 好答案,我赞成。问题:为什么要返回 access 的引用?
  • 我已经添加了解释。
  • 你不能用多个参数重载operator[],你需要改用operator()
  • +1 表示努力,但有两个注释:要使用数组“look-and-feel”,可以提供一个内部类 Row,使Data[3][5] 之类的东西成为可能。只是一个不错的选择,但不是处理 2D 所必需的。我想提的另一件事是:DON'T 子类向量。如果您在没有非常小心地使用派生类的情况下从它派生,它不会被设计为导致拼接和类似错误的基类。没有理由选择子类而不是封装(“懒得写转发方法”不算理由)。
  • 感谢@Arne,您的意见非常宝贵。我也更喜欢封装向量,因为通常无论如何都应该从一个类中访问它,然后一个简单的内联私有方法就足够了。
【解决方案5】:

尝试使用向量。

#include <iostream>
#include <vector>
using namespace std;

int main()
{
// An empty vector of vectors. The space 
// between the 2 '>' signs is necessary
vector<vector<int> > v2d;

// If you intend creating many vectors 
// of vectors, the following's tidier
  typedef vector<vector<int> > IntMatrix;
  IntMatrix m;

// Now we'll try to create a 3 by 5 "matrix".
// First, create a vector with 5 elements
   vector<int> v2(5, 99); 

// Now create a vector of 3 elements. 
 // Each element is a copy of v2
 vector<vector<int> > v2d2(3,v2);

// Print out the elements
 for(int i=0;i<v2d2.size(); i++) {
  for (int j=0;j<v2d2[i].size(); j++)
    cout << v2d2[i][j] << " "; 
    cout << endl;
   }
}

【讨论】:

  • Nitpick:从最新的 C++ 标准开始,两个 &gt; 之间的空格不再需要。
  • @Brennan Vincent 这对我来说是一个新信息。谢谢。
【解决方案6】:

您可以通过以下方式使用 c++ new[] 分配内存:

int** array = new int*[x];
for(int i = 0; i < x; i++)
    aryay[i] = new int[y];

这个简单的概念是它和指向数组的指针数组。它也必须删除以避免内存泄漏。

【讨论】:

  • 我会复制我之前的评论:如果他要求 3d 数组怎么办?还是4d?
  • @Bartek 公平地说,2D 是一种特殊情况,它比更高维度更频繁地出现大量
  • 第二个问题是二维数组。
【解决方案7】:

虽然“向量的向量”解决方案可以令人满意,但它们不是 2D 结构:每个“行”将独立分配,并且长度可以逐行不同(因此必须保持 2D 约束手动:插入一个项目需要放大所有行,以便一个在另一个之上的元素保持它们的相对位置。

如果您需要适当的动态 2D 结构,您可以将向量(简单的一维)包装在一个类中(我们称之为“table”)并为该类提供正确维护它所需的操作外部表示的 2D。特别是:

  • 定义一个Colums() constRows() const 函数来重新调整表的实际大小。
  • 定义一个 int&amp; operator()(unsigned row, unsigned col) { return vect[row*Colums()+col]; } 并且它是 const 对应项
  • 定义一个struct coord { unsigned r, unsigned c } 和一个方便的table::operator[](const coord&amp;)
  • 定义insertremove方法来插入一行(只需在r*Columns()处插入Colums()连续元素)和一列(从c开始,每Columns()+1插入一个元素)
  • 定义适当的迭代器类,沿着列(++ 前进 1 个元素)和行(++ 前进 Colums() 元素)(注意始终为它们定义 end() 值)

所有这些东西的关键是对第二点n = r*C+c公式的理解。其他一切都只是一个几乎直接的结果。

【讨论】:

  • 不错。不过,为了简单起见,我会将 T&amp; 更改为 int&amp;
  • @BartekBanachewicz:是的!我只是隐含地认为它是一个模板......鉴于上下文,我按照建议进行更新!
【解决方案8】:
unsigned **numbers;
numbers = new unsigned*[a];
for (int i=0; i<a; i++){
    numbers[i] = new unsigned[b];
}

它的行为与二维数组不完全一样,因为它在内存中不连续,但它会这样做。您还可以使用符号numbers[i][j] 来访问元素。

记住delete[]numbers的每个元素在delete[]-ing numbers本身之前的末尾

使用std::vector 可能是一个更可取的解决方案,详见其他帖子

【讨论】:

  • 如果他要一个 3d 数组怎么办?还是4d?
  • 他没有。我为这个问题提供了一个解决方案。但我确实同意你的观点,它不能很好地概括。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-02-01
  • 1970-01-01
  • 2012-02-05
  • 1970-01-01
  • 2018-10-12
相关资源
最近更新 更多