如何在不使用shared_ptr

时间:2015-11-18 07:45:01

标签: c++ arrays destructor shared-ptr

我正在实现一个树,每个节点都有一个节点**用于儿子:

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

为了插入新的儿子我找到了一种方法,而不是制作新的[]数组Node *并删除旧的(我不能使用列表,我被重新修复......)。但是当使用delete []删除旧节点**时,即使我将指针保存到另一个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);
}

3 个答案:

答案 0 :(得分:4)

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[]释放其内容。< / p>

没有必要涉及引用计数来解决这类问题。只需分配一个新数组(不触及sons),将sons的内容复制到新的更大的数组,然后然后释放sons的内存make 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指针开始,指向包含“父亲”的一些“儿子”的内存块(对于节点系统非常重男轻女的命名约定,顺便说一句)。

enter image description here

然后我们分配一个稍微大一点的新内存块,new_sons指向

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

enter image description here

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

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

enter image description here

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

new_sons[sonsNum++] = sn;

enter image description here

现在我们有了副本,我们可以丢弃旧数据。

// Delete old data.
delete[] sons;

enter image description here

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

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

enter image description here

......你已经完成了。

答案 1 :(得分:2)

当你这样做时

Node** tmpSons = sons;

它不会仅仅将指针复制到实际内存中,这意味着你有两个指向同一内存的指针。

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

答案 2 :(得分:2)

  

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

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

  

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

当然,在 之后删除{/ 1}} 你已经复制了它的内容。

  

我不能使用清单,我已经恢复了......

我建议使用矢量。