如何在不使用 shared_ptr 的情况下不删除指针的值

How to not delete pointer's value without using shared_ptr

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

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

为了插入新子,我找不到一种方法,而不是创建 Node* 的新 [] 数组并删除旧的(我不能使用列表,我被限制了...)。但是当使用 delete[] 删除旧的 Node** 时,即使我已经将指针保存在另一个 tmp 数组中,它的值也会消失! (甚至 Node destrucor 都是空的!为什么?)。所以如果我使用 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);
}

当你这样做时

Node** tmpSons = sons;

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

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

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 的内容复制到新的更大的数组,然后 然后 释放 [=18 的内存=] 并使 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 指针开始,它指向一个包含一些 "sons" 或 "father" 的内存块(顺便说一句,节点系统的父权制命名约定)。


然后我们分配一个新的,稍微大一点的内存块,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;

...大功告成。

but when deleting the old Node** using delete[], even I have saved the pointers inside to another tmp array, its values will be gone! (even Node destrucor is empty! why?)

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

is there a way to do that without shared_ptr?

当然,在复制内容后删除tmpSons

I cant use list, I am restrected...

我推荐使用矢量。