3

次のコードでは、リターン 1 の後に次の実行時例外 (おそらくメモリ リーク) が発生しています。および Node() のデストラクタで。

Unhandled exception at 0x0f9bad4a (msvcp100d.dll) in test.exe: 0xC0000005: Access violation reading location 0xfeeefef2.

smart_ptr を使用してからしばらく経ちましたが、ここで何が間違っているのかを学ぼうとしていますか?

#include <vector>
#include <queue>
#include <memory>

#include <iostream>
using namespace std;

class Node;
typedef shared_ptr<Node> SharedNode;

class Node {
    Node* parent;
    vector< SharedNode > children;
    int value;

    //limiting construction
    Node(int a_value):value(a_value),parent(0){}
    Node(const Node &copy); //non-construction-copyable
    Node& operator=(const Node& copy); //non-copyable
public:
    static SharedNode create(int a_value){
        return SharedNode(new Node(a_value));
    }
    SharedNode addChild(SharedNode child){
        child->parent = this;
        children.push_back(child);
        return child;
    }

SharedNode getNode(int searchValue);
};

SharedNode Node::getNode(int searchValue){

    // Breadth First Search
    queue<SharedNode> que;
    que.push(SharedNode(this));

    while(!que.empty()){
        SharedNode node = que.front();
        que.pop();

        if(node->value == searchValue)
            return node;

        vector<SharedNode>::iterator it;
        for(it = node->children.begin(); it != node->children.end(); it++){
            que.push(*it);
        }
    }

    return 0;
}

int main(){
    SharedNode node_ptr = Node::create(5);

    for(int i  = 0; i < 4; ++i)
        node_ptr->addChild(Node::create(i));

    cout << (node_ptr->getNode(-1) != 0 ? "Found" : "Not found");

    return 1;
}

これで shared_ptr を使用すると、次のようにめちゃくちゃになっていると思いますshared_ptr(this)。しかし、それは私の推測です。

ここで何が間違っていますか?

4

2 に答える 2

6

問題は

que.push(SharedNode(this));

これにより、現在所有している新しい共有ポインタが作成されますthis。ただし、create()メソッドが原因で、同じオブジェクトを所有する別の共有ポインターが存在します。これにより、二重削除が発生する可能性があります。

この状況で共有ポインターを使用する理由がある場合、正しい解決策はenable_shared_from_thisです。

まず、ノード定義をこれに変更します。

class Node : public std::enable_shared_from_this<Node> { ...

次に、問題のある行を次のように変更します

que.push(this->shared_from_this());

これにより、オブジェクトを指す shared_ptr が返されますが、2 つの個別の shared_ptr オブジェクトではなく、既存の shared_ptr と共有されます。

の使用this->shared_from_this()が正当であるためには、オブジェクトが shared_ptr によって所有されている必要があることに注意してください。静的create()メソッドを使用して既にこれを達成していますが、制限を理解していることを確認したかったのです。

編集:所有権の簡単な説明shared_ptr

コンストラクターを使用して raw ポインターからを作成するshared_ptrと、オブジェクトへのポインターと参照カウントの両方を含む参照オブジェクトが作成されます。これは、それを指すオブジェクトの数を決定するために使用されshared_ptrます。次に、この参照オブジェクトへのポインターが、その元の から作成されたすべてのコピーに渡されshared_ptr、参照カウントはそれを参照するオブジェクトの数を追跡しshared_ptrます。

を呼び出すと、共有ポインタが別の共有ポインタによって所有されshared_ptr(this)ていることを知る方法がなく、新しい参照オブジェクトが作成されます。thisそれらの 1 つが参照カウント 0 に達すると、他のshared_ptr参照オブジェクトがまだそのオブジェクトを指しているにもかかわらず、オブジェクトが削除され、ダングリング ポインターと表示されているエラーが発生します。

親が存在する場合にのみ子が存在する必要がある場合は、ノードを変更して、単にstd::vector他のノードを持つようにすることを検討します (ポインターを削除します)。最高レベルのノードがそのデストラクタを介して破棄されると、ベクトルが破棄され、子ノードが破棄されます。

class Node
{
  // whatever operations you need... 

  std::vector<Node> children;
}

編集:要求どおり...

子を親よりも長生きさせたいユースケースがある場合は、子の前に破棄される可能性があるため、親ポインターを処理する必要があります。簡単な解決策の 1 つは、親ポインターが本当に必要かどうかを判断し、不要な場合は削除することです。

ただし、それでも保持したい場合は、shared_ptrここでは使用できません。これを行うと、循環依存関係が発生し、どちらも自動的に破棄されません。これは望ましくありません。

ここでの解決策は、 を使用することstd::weak_ptrです。基本的に、それはshared_ptr参照オブジェクトと相互作用し、指し示したオブジェクトの破壊を妨げません。

class Node
{
private:
   std::weak_ptr<Node> parent;
   // Other constructors.  
   Node(int a_value):value(a_value),parent() {} 
public:
   SharedNode addChild(SharedNode child){
        child->parent = this->shared_from_this(); // Initialize their pointer using
                                                  // your shared pointer
        children.push_back(child);
        return child;
   }
   // This function will return a shared_ptr to nullptr (and will evaluate false) 
   // if you have no parent, or if the parent node has been deleted
   SharedNode getParent()
   {
       return parent.lock();
   }
};
于 2012-07-27T19:15:06.360 に答える
4

次のコードで何が起こるか考えてみましょう:

Node * dumb_ptr = new Node;
shared_ptr<Node> smart1 = dumb_ptr;
shared_ptr<Node> smart2 = dumb_ptr;

これで、同じオブジェクトを所有していると考える 2 つのスマート ポインターができました。そのうちの 1 つはオブジェクトを削除しようとし、もう 1 つはその削除されたオブジェクトをある時点で使用または削除しようとします。これを修正する方法は、常に別のスマート ポインターまたは からスマート ポインターを作成することnewです。ダムポインターをまったく使用しないことをお勧めします-これにはthis.

shared_ptr<Node> smart1 = new Node;
shared_ptr<Node> smart2 = smart1;
于 2012-07-27T19:01:22.980 に答える