【问题标题】:I need to resize an array of pointers我需要调整指针数组的大小
【发布时间】:2012-11-28 23:18:53
【问题描述】:

假设我想调整一个 int 指针数组的大小,我有一个看起来像这样的函数

    template<typename T>
static void Resize(T* arr, UINT oldsize, UINT newsize) {
    T* ret = new T [newsize];
    memcpy(ret, arr, sizeof(arr[0]) * oldsize);
    delete[] arr;   
    arr = ret;
};

问题开始于我尝试调整使用“new”关键字创建的元素数组的大小(即使类内的数据本身是 POD),因为 delete[] 触发了它们的解构函数,然后离开新数组带有指向不再存在的对象的指针。所以..即使对象是用“new”创建的,我不能只使用 free 命令来摆脱旧数组吗?还是以某种方式删除数组而不触发每个成员的解构器?

【问题讨论】:

  • 如果arr 被声明为T*&amp; arr,这将正常工作(假设T 是可简单复制的并且具有noexcept 默认构造函数)。
  • 如果您要编写这样的潜在危险代码,那么只需使用mallocfree。由于memcpy,您的代码无法处理非平凡类型,因此使用newdelete 没有多大意义。
  • @Pubby:或者我们可以建议std::copy,以便正确完成,而不是更不正确
  • the delete[] triggers their deconstructor which then leaves the new array with pointers to objects that dont exist anymore - 否; delete[] 破坏了原始指针,而不是它们指向的 ints。
  • 我切换到向量,比我想象的要容易。还得删掉我 3/4 的代码,因为显然大部分代码都花在了管理数组上 T_T

标签: c++


【解决方案1】:

使用std::vector


编辑:应大众需求,解释为什么 OP 的代码不起作用

代码:

template<typename T>
static void Resize(T* arr, UINT oldsize, UINT newsize) {
    T* ret = new T [newsize];
    memcpy(ret, arr, sizeof(arr[0]) * oldsize);
    delete[] arr;   
    arr = ret;
};

这里arr是一个按值传递的指针。最后分配给arr 只会更新实际参数的本地副本。因此,在此之后,调用代码中的实际参数指向一个数组,该数组一直是deleted,结果非常灾难性!

可以通过传递指针通过引用来拯救它:

template<typename T>
static void Resize(T*& arr, UINT oldsize, UINT newsize) {
    T* ret = new T [newsize];
    memcpy(ret, arr, sizeof(arr[0]) * oldsize);
    delete[] arr;   
    arr = ret;
};

但这仍然是相当脆弱的代码。

例如,调用者需要跟踪数组大小。

使用名为 astd::vector,resize 调用将改为

a.resize( newSize )

与 DIY 解决方案相比,当 newSize 较大时,向量的那些额外元素将被清空(这比将它们保留为不确定值更安全一些)。

std::vector 可以像原始数组一样被索引。有关如何使用它的更多详细信息,请参阅您的 C++ 教科书。如果您还没有 C++ 教科书,那就买一本吧:对于大多数人来说,从网络上的文章和问答中学习 C++ 只是一个不切实际的提议。

【讨论】:

  • 好的,我是一个新的反对者,这就是我给你 -1 的原因;你没有回答问题。这显然是一个没有太多经验的人,也许是初学者。 “使用向量”本身就是垃圾建议,因为它无助于 OP 理解他的代码是如何工作的。我不想与不了解指针和动态内存分配如何工作的人一起工作,因为他们总是被告知“使用向量”。 在您回答问题之后给出很好的建议(带有解释),但您应该先回答问题。
  • 我不确定你的意思。我要说的是,我经常看到向初学者分发“使用矢量”这样的建议而没有任何解释。他们需要首先了解为什么您会在数组上使用向量,它解决了哪些问题等。如果不了解抽象存在的原因,他们只是在编写货物崇拜代码并且永远无法解决他们软件中真正困难的、较低级别的问题。
  • @Cheersandhth.-Alf:我不知道你是一个非常固执、傲慢的人,还是被巨魔攻击了。如果你坚持 that 是一个“完美”的答案,那你就大错特错了。如果您添加了一个很好的解释,为什么 OP 应该切换到 std::vector 忽略您甚至没有尝试回答实际问题的事实是可以容忍的。
  • 正是我的观点——我不是提出论据,只是观察到你发表的每篇文章似乎都会引起敌意。你把那个观察结果变成了某种指责。太棒了。
  • @Cheersandhth.-Alf:引用 OP:“我想调整 int 指针数组的大小”。我认为这很清楚地表明他不使用 std::vector 并为他的 pointers 问题寻找解决方案。我没有说 std::vector 不是解决方案,它肯定是!但在这种形式下,这是一个非常糟糕的答案,但也没有直接回答问题。
【解决方案2】:

就其价值而言,您尝试做的事情并不是一件非常糟糕的事情,而且有时它是有道理的,但是newdelete提供的接口根本不支持它(或new[]delete[])。正如其他人所说,mallocfreerealloc支持(需要注意的是 realloc 将在重新分配时复制指针值,但不能保证新区域中的指针被初始化为任何有用的东西,比如NULL)。

因此,事不宜迟,适用于几乎所有人的最简单答案是使用std::vector&lt;int&gt;,而不是尝试自己管理内存。矢量具有调整大小的能力,当它调整大小时,它会复制需要复制的东西。 std::vector 的存在是为了提供“可调整大小的数组”并为您管理内存。实际上,如果你想要一个指针容器,最好使用 std::vector&lt;std::unique_ptr&lt;T&gt;&gt;/std::vector&lt;std::shared_ptr&lt;T&gt;&gt;(在 C++11 中)或来自 Boost Pointer Container 的东西。

不管怎样,原来的 STL 并没有使用new[]delete[] 来实现std::vector&lt;T&gt;。通常,操作系统提供的内存远远超过您的要求。例如,如果我尝试malloc 16 字节,我返回的块很可能是 1024 字节。我可以用它来存储四个 32 位整数。当我用完空间时,我可能会要求 32 个字节,并得到一个 1024 字节的不同块,我可以将我的四个整数复制到其中。但是,当原始块实际上大到可以开始时,为什么还要麻烦要求一个新块来保存我的整数呢?不幸的是,new[]delete[] 没有提供一种方式来说“给我一个至少这么大的块,顺便说一句,这里有一个可能已经足够大的块”。 realloc 有点像。 Facebook Folly includes a std::vector-like container 不使用 new[]delete[](请注意,它也不使用 realloc,因为这通常不适用于对象容器;相反,它使用 malloc 和非标准函数 malloc_usable_size) 和 Firefox goes through the trouble 以类似的方式管理内存。

即便如此,我也不鼓励尝试变得棘手。 std::vector 有很多满意的用户。

【讨论】:

    【解决方案3】:

    我认为在删除之前使数组中的所有元素都指向 NULL 应该可以完成这项工作。但是,如果您可以使用 STL,那么 std::vector 会让您的生活更轻松(正如 Alf 建议的那样:P)。

    【讨论】:

    • 我遇到的问题是我有一个 A 类,我似乎无法将 std::vector 添加到该类中,它说“没有使用这种类型定义的成员”
    • @JakeFreelander,您需要向我们展示一些代码,说明您使用std::vector&lt;A*&gt; 作为会员所做的努力。当然可以,所以只是语法正确的问题。
    • 我搞定了,不过我对向量很陌生,我需要在解构函数中删除向量吗?
    • @JakeFreelander:不,你不需要删除std::vector。当它超出范围时它会自我毁灭。
    猜你喜欢
    • 1970-01-01
    • 2023-03-07
    • 1970-01-01
    • 2012-03-17
    • 2016-11-17
    • 1970-01-01
    • 2023-03-24
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多