1

言いたいことがたくさんあります。まず、以下のアプローチがデザインパターンであるのか、それとも一般的なテクニックであるのかを知りたいのですが、それで、タイトルに関する詳細な情報は得られませんでした。それが事実なら、名前は何ですか? とにかく、これは私が達成しようとしているものの縮小バージョンです。私はコピーを使用する必要があるので、私はstd :: shared_ptrを使うことが、割り当てられていない(削除された)ポインタを避けるのが最善であることを発見しました。継承とスマートポインタ(std :: shared_ptr)

class Foo 
{ 
public: 
    Foo() : ptr(nullptr) {} 
    Foo(const Foo& foo) : ptr(foo.ptr) {} 
    virtual ~Foo() = default; 

    void whatever() { 
     if (ptr) 
      ptr->whateverHandler(); 
    } 

    void reset() { 
     ptr.reset(); 
    } 

    void resetBar() { 
     ptr.reset(new Bar); 
    } 

    // Other resets here... 

protected: 
    Foo(Foo* foo) : ptr(foo) {} 

private: 
    // Every child class should override this 
    virtual void whateverHandler() { 
     throw "whateverHandler cant be called within base class"; 
    } 

protected: 
    std::shared_ptr<Foo> ptr; 
}; 

class Bar : public Foo 
{ 
public: 
    Bar() : Foo(this) {} 
    void whateverHandler() { 
     printf("Bar's handler!!! \n"); 
    } 
}; 

これはすべてうまく見えてコンパイルされますが、以下のexameがクラッシュします。何故ですか?

int main() 
{ 
    { 
     Foo f; 

     f.resetBar(); 
    } 

    return getchar(); 
} 
+0

は 'Bar'が破壊されると、その' Foo'は二回 – Danh

+0

あなたは 'のstd :: enable_shared_from_this'を必要とするかもしれない破壊されます。しかし、この特定の例では、それを必要としません。 – Danh

+1

仮想デストラクタもありません。 –

答えて

6
Bar() : Foo(this) {} 

あなたはshared_ptrthisを渡すときは注意してください。

f.resetBar();ptr.reset(new Bar);の後に何が起こるかを考え直してください。 new Barについて

  1. 、タイプBarのオブジェクトが構築され、そのコンストラクタ内部thisが親クラスのメンバptrに渡され、その後、オブジェクトはstd::shared_ptrあり、それによって管理されます。

  2. その後、オブジェクトはf.ptrで管理されます。別のstd::shared_ptrです。

だからそこにいる2つのstd::shared_ptr sが同じオブジェクトを指しますが、std::shared_ptr sがそれについて何も知りません。別々に構築しているからです。 ff.ptrが破壊されると、指し示されたオブジェクトも破壊されます。その後、メンバーptrが破壊され、同じオブジェクトを再び破壊しようとします。これがUBにつながります。

デザインが何を達成しようとしているのか分かりませんが、thisstd::shared_ptrに渡すのを止めるだけでUBを削除できます。

class Foo 
{ 
public: 
    virtual ~Foo() = default; 
    void whatever() { 
     if (ptr) 
      ptr->whateverHandler(); 
    } 
    void reset() { 
     ptr.reset(); 
    } 
    void resetBar() { 
     ptr.reset(new Bar); 
    } 
    // Other resets here... 
private: 
    // Every child class should override this 
    virtual void whateverHandler() = 0; 
    std::shared_ptr<Foo> ptr; 
}; 

class Bar : public Foo 
{ 
public: 
    void whateverHandler() { 
     printf("Bar's handler!!! \n"); 
    } 
}; 

int main() 
{ 
    { 
     Foo f; 
     f.resetBar(); 
     f.whatever(); 
     f.resetSthElse(); 
     f.whatever(); 
    } 
} 

そしてIMO、派生クラスを指すタイプstd::shared_ptrのメンバーを有する混乱です。それを分離する方が良いかもしれない。そして、私はそれがbridge design parternかもしれないと思います。 はFoo :: ptrはを求めてのFooを呼び出すためFoo::ptrが参照カウント1

Foo::resetBar()でとその母親Foo(this)へのポインタを保持している

class Foo 
{ 
public: 
    void whatever() { 
     if (ptr) 
      ptr->whateverHandler(); 
    } 
    void reset() { 
     ptr.reset(); 
    } 
    void resetBar() { 
     ptr.reset(new BarHandler); 
    } 
    // Other resets here... 
private: 
    std::shared_ptr<FooHandler> ptr; 
}; 

class FooHandler 
{ 
public: 
    virtual ~FooHandler() = default; 
    // Every child class should override this 
    virtual void whateverHandler() = 0; 
}; 

class BarHandler : public FooHandler 
{ 
public: 
    void whateverHandler() { 
     printf("Bar's handler!!! \n"); 
    } 
}; 

int main() 
{ 
    { 
     Foo f; 
     f.resetBar(); 
     f.whatever(); 
     f.resetSthElse(); 
     f.whatever(); 
    } 
} 
+0

私は本当の問題に合うように私のポストを少し変更しました(古いものは間違って記載されていて、あなたはそれに答えました)。なぜ、 'd.resetFree()'がクラッシュするのですか? –

+0

@ YvesHenri何も変更されていません。 'f.resetBar();'と 'ptr.reset(new Bar);'に対して、 'f.ptr'は構築されたオブジェクトを指し示します。これは、自身の親メンバ' ptr'によって指し示されます。 'Foo'と' Bar'型の2つのオブジェクトがあり、両方のメンバ 'ptr'が同じオブジェクトを指していることに注意してください。 – songyuanyao

+0

私は何を求めているのですか?私がスマートポインタを選んだのは、コピーの上にベースの内部ポインタを渡す必要があるからですが、それが削除されたかどうかを安全に検出する必要があるからです。生のポインタのためにそれを変更して、割り当てを解除すれば、古いバージョン(私が試したもの)が得られます。 2つのバーが宣言されているが、コピーコンストラクタ(内部ポインタを取得)で作成されたものの、コピーされたBar(範囲外など)を破棄したとします。コピーは次の '何でも'の呼び出しでクラッシュします!私はこのデザインが達成しようとしているかわからないんだけど:( –

0

reset(new Bar)はFoo :: ptrはを与えましたその母親にその所有権をFoo(this)と参照カウントがすでに0に減少したことがわかったので、それは殺す必要があるFoo

Fooが死んだとき、その子は殺されます。だからFoo :: ptrも死んでいなければなりません。その後、new Barを無効にすると、Foo::ptrがUBになります。