0

shared_ptr<Base>派生クラスを持つある種のツリーリストに使用しています。しかし、ツリーが破壊されると、ポインターアクセス違反が発生します。

私のコードは次のようになります。さらに、これは実際に私のランタイムエラーをシミュレートします。

#include <iostream>
#include <memory>
#include <vector>


class Base;
typedef std::shared_ptr<Base> pBase;
class Derived;

class Base {
public:        
    std::vector<pBase> children;

    pBase parent;

    Base() {}
    virtual ~Base() {}

    virtual void doSomething() {}
    void add(pBase i);
};

class Derived : public Base {
    void doSomething() {
        // Do something...
    }
};

void Base::add(pBase i) {
    i->parent = pBase(this);
    children.push_back(i);
}


int main() {
    pBase tree = pBase(new Derived());
    pBase child(new Derived());
    child->add(pBase(new Derived()));
    tree->add(child);
}

また、次の行を次のように追加すると、次のようになりますBase::~Base。std :: cout << "destruct" << name << std :: endl;

Baseそして、インスタンスごとに異なるnameというstd :: stringを実装すると、デストラクタが複数回呼び出されていることがわかります(Base::parent参照のためだと思います)。もちろんそれが私のエラーを引き起こしましたが、shared_ptr<Base>実際にそれを破壊する前にその参照を数えることが期待されているので、なぜそれが起こるのかまだわかりません!!?

私が間違っていることを誰かが教えてくれることを願っています!しかし、もっと重要なのは、これをどのように修正できるかです!

4

3 に答える 3

3

add()でこの行を見てください

i->parent = pBase(this);

addを呼び出すたびに、への新しい共有ポインタを作成しますthis。これらの共有ポインタは別個のものです。つまり、あなたが思うように「共有」されていません。したがって、子を初めて削除すると、その親が削除されます(共有ポインターであるため)。したがって、コードが爆発します。

(最初に)親を単純なダムポインタにしてみてください。

Base *parent;
于 2013-01-16T19:23:40.373 に答える
3

他の人の答えに追加するだけです:あなたが行でやりたいことをするための標準的な方法

i->parent = pBase(this);

を使用することstd::enable_shared_from_thisです。君

  1. それから派生Baseする

    class Base : std::enable_shared_from_this<Base> {
    
  2. Baseすべてのインスタンスがによって所有されていることを確認してくださいstd::shared_ptr。次のような式でオブジェクトを作成するので、あなたの場合は問題ありません。

    pBase child(new Derived());
    
  3. が必要な場合shared_from_this()の代わりに使用します。問題のある行は次のようになりますthisstd::shared_ptr

    i->parent = shared_from_this();
    
于 2013-01-16T19:49:05.870 に答える
2

ここ

i->parent = pBase(this);

プレーンな古いポインタから、新しいポインタから直接取得しなかったオブジェクトへのスマートポインタを作成します。これは絶対にしないでください。

@Roddyが説明したように、個別の参照カウンターを備えた個別のスマートポインターオブジェクトを取得します。1つのポインティの2つの参照カウンターは機能しません。

あなたの場合、@ Roddyが提案したように、親を通常のポインタにすることはおそらく問題ありません。このようにして、循環参照で問題が発生することはありません。親を削除した後は、親ポインタにアクセスしないように注意してください。親と一緒にすべての子を削除しても問題ありません(これは、他の場所にさらにスマートポインターを格納しない限り、自動的に行われます)

スマートポインタを初期化する場合は、基本的に2つの選択肢があります。すべてのインターフェイスでスマートポインタを使用します。残念ながら、これは暗黙のパラメータであるため、「this」では機能しません。すでに作成したスマートポインターを、追加のパラメーターで手動でメソッドに渡す必要があります。このような:

tree->add(tree, child);

これはちょっと醜いので、静的メソッドを「追加」することを検討することをお勧めします。そうすれば、親を2回渡す必要がなくなります。

他の選択肢:boost::intrusive_ptrのような別の種類のスマートポインターを使用します。ここで参照カウントをポインターに格納できます。このようにして、「this」のようなダムポインタしかない場合でも、参照カウントを見つけることができます。

編集:以下の@jpalecekによる回答の方が優れています。それを使用してください。セバスチャン。

于 2013-01-16T19:23:39.920 に答える