【问题标题】:How to not delete pointer's value without using shared_ptr如何在不使用 shared_ptr 的情况下不删除指针的值
【发布时间】:2016-02-19 20:24:41
【问题描述】:

我正在实现一棵树,每个节点里面都有 Node** 供儿子使用:

class Node {
    string word;
    Node* father;
    Node** sons; 
    int sonsNum;
    ....
}

为了插入新儿子,我无法找到一种方法,而不是制作 Node* 的 new[] 数组并删除旧的(我不能使用列表,我被重新调整了......)。但是当使用 delete[] 删除旧的 Node** 时,即使我已经将指针保存到另一个 tmp 数组中,它的值也会消失! (甚至 Node destructor 也是空的!为什么?)。所以如果我使用 shared_ptr 我认为它会解决它,有没有办法在没有 shared_ptr 的情况下做到这一点?

void insertSon(Node* sn) {
    sn->father=this;
    Node** tmpSons = sons;  //should be shared_ptr? but I dont want that
    if(sons)
        //delete[](sons);   // after this line, tmpSons has garbage!
    sons = new Node*[sonsNum+1];
    for(int i=0 ; i<sonsNum ; i++) {
        sons[i]=tmpSons[i];
    }
    sons[sonsNum]=sn;
    sonsNum++;
} 

编辑: 抱歉忘了说我想要节点内的真实值,所以我不能复制。 (此代码中的字符串仅用于示例......它是另一个真实的对象......)

编辑: 解决方案:

void insertSon(Node* sn) {
    sn->father=this;
    Node** tmpSons = new Node*[sonsNum];    
    for(int i=0 ; i<sonsNum ; i++) {
        tmpSons[i]=sons[i];
    }
    if(sons)
        delete[](sons);
    sons = new Node*[sonsNum+1];
    for(int i=0 ; i<sonsNum ; i++) {
        sons[i]=tmpSons[i];
    }
    sons[sonsNum]=sn;
    sonsNum++;
    delete[](tmpSons);
}

【问题讨论】:

  • 您只需复制指针的值并删除它指向的内容。指针不计入引用计数。
  • 抱歉忘了说我想要节点内的真实值
  • 为什么不直接分配指针而不删除原来的?
  • 因为我需要更大的。所以我需要新的[old_size +1]。然后必须删除旧的!
  • 你可以把节点放在vector&lt;Node*&gt;

标签: c++ arrays destructor shared-ptr


【解决方案1】:
Node** tmpSons = sons;  //should be shared_ptr? but I dont want that
if(sons)
    //delete[](sons);   // after this line, tmpSons has garbage!

是的,这很正常——tmpSons 的内容将失效,因为它只是指向与sons 相同的内存,而您正在使用operator delete[] 释放其内容。

没有必要涉及引用计数来解决这类问题。只需分配一个新数组(不涉及sons),将sons 的内容复制到新的更大的数组,然后然后释放sons 的内存并使sons 指向新区块。关键是在将 sons 的内容复制到新数组之前不要释放它。这就像你不想扔掉你正在复制的CD,直到你复制它(你的原始版本甚至在复制之前就把它扔掉了)。像这样的:

void insertSon(Node* sn) {
    sn->father = this;

    // Create a new array and copy the old data.
    Node** new_sons = new Node*[sonsNum+1];
    for(int i=0; i<sonsNum; i++)
        new_sons[i] = sons[i];
    new_sons[sonsNum++] = sn;

    // Delete old data.
    delete[] sons;

    // Point to the new data.
    sons = new_sons;
}

在您开始担心异常安全之类的问题之前,这应该会让您坚持下去,此时您可能确实希望避免过度依赖这些手动内存管理技术并使用更多符合 RAII 的对象。

视觉分解

这是一个视觉细分。首先,我们从 sons 指针开始,它指向一个包含“父亲”的一些“儿子”的内存块(顺便说一句,节点系统非常父权制的命名约定)。


然后我们分配一个新的、稍大的内存块,new_sons 将指向它:

Node** new_sons = new Node*[sonsNum+1];


接下来我们将之前的子条目复制到新数组中。

for(int i=0; i<sonsNum; i++)
    new_sons[i] = sons[i];


...并添加我们的新条目。

new_sons[sonsNum++] = sn;


现在我们有了一个副本,我们可以扔掉旧数据了。

// Delete old data.
delete[] sons;


...最后但同样重要的是,我们可以让sons 指向新数据。 new_sons 将超出范围,指针也将被销毁(不是它指向的东西,只是指针),我们最终会得到我们想要的(sons 现在指向一个新数组, 大一个条目,同时包含旧条目和我们添加的新条目)。

// Point to the new data.
sons = new_sons;

...你就完成了。

【讨论】:

  • 我们不想在这个函数中使用delete[] new_sons,因为它最终会被sons指向——基本上就像我们复制了一张CD,现在我们可以抛出旧的,只使用新的,但你也不想扔掉新的,因为我们将通过sons 最终使用新的(稍微大一点的),它现在将指向新的。
  • 我感到很无聊,所以我对代码在指针级别所做的事情进行了可视化分解。我认为这可能有助于您更好地理解指针和它们指向的内存内容之间的分离。
【解决方案2】:

当你这样做时

Node** tmpSons = sons;

它不会复制实际内存本身,只复制指针,这意味着您有两个指针都指向同一个内存。

如果你对其中一个指针执行delete[],那么另一个指针将变成一个流浪指针,因为它现在指向未分配的内存。取消引用任何指针都会导致未定义的行为

【讨论】:

  • @Atheel 你无法避免使用std::shared_ptr 仅用于tmpSons,你也需要将它用于sons 以及任何你使用这个指针的地方。智能指针只包装 pointer 而不是它指向的内存。此外,您应该更多地从资源所有权的角度来看待智能指针,一个资源是否可以同时拥有多个所有者然后使用std::shared_ptr,或者资源一次只有一个所有者然后使用std::unique_ptr
  • 确实可以!你的回答让我有了想法!谢谢。查看我的解决方案编辑
【解决方案3】:

但是当使用 delete[] 删除旧的 Node** 时,即使我已经将指针保存到另一个 tmp 数组中,它的值也会消失! (甚至 Node 析构函数都是空的!为什么?)

但是您还没有将指针保存到另一个数组中。在删除 Node** 后执行此操作。在您删除某些内容后,访问它的内容会有未定义的行为。

有没有办法在没有 shared_ptr 的情况下做到这一点?

当然,在您复制了tmpSons 之后删除它的内容。

我不能使用列表,我被rerectrected...

我建议使用矢量。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-06-25
    • 1970-01-01
    • 2020-07-13
    • 2014-04-30
    • 1970-01-01
    • 1970-01-01
    • 2021-01-14
    相关资源
    最近更新 更多