2017-01-23 244 views
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) {} 

當你通過thisshared_ptr要小心。

想想在f.resetBar();ptr.reset(new Bar);之後會發生什麼。

  1. 對於new BarBar類型的對象將被構造,並且它的構造內this被傳遞到父類構件ptr,那麼該目的通過其是std::shared_ptr的它進行管理。

  2. 之後,該對象由f.ptr管理;這是另一個std::shared_ptr

所以這裏有兩種std::shared_ptr指點下,以相同的對象,但std::shared_ptr■不要知道呢;因爲你在分別構建它們。當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(); 
    } 
} 

和國際海事組織,具有std::shared_ptr類型的成員指向派生類是混亂;分開它可能會更好。然後,我認爲它可能是bridge design partern

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'類型的對象,都是成員'ptr'指向同一個對象。 – songyuanyao

+0

那麼我怎樣才能實現我在找的東西呢?我選擇智能指針的原因是因爲我需要在拷貝上傳遞基本的內部指針,但是我需要安全地檢測它是否被刪除。只需將其更改爲原始指針並執行釋放操作,然後您將擁有舊版本(我嘗試過)。要清楚,假設你已經聲明瞭2條,但是通過複製構造函數(檢索內部指針)創建了一條,但是你銷燬了複製的條(例如超出了範圍)。現在這個副本會在下一個'whatever'調用中崩潰! :( –

0

Foo::ptr具有指向其母親Foo(this)參照計數1

Foo::resetBar(),當富:: PTR調用reset(new Bar)富:: PTR了將其所有權轉移給其母親Foo(this)並發現引用計數已降至0,因此需要殺死Foo

Foo死亡時,其子女被殺害。所以Foo :: ptr也必須是死的。然後將new Bar分配給死亡Foo::ptr會導致UB。