簡體   English   中英

C ++ Singleton類-繼承優良作法

[英]C++ Singleton class - inheritance good practice

在現有項目中,我將繼承聲明為Singleton的Controller類(MVC),以便定義自己的處理方式。 如何恰當地推導這個Singleton類?

首先,我擴展了上下文,並且需要這種繼承。

我添加到現有軟件中的應用程序希望使用一個MVC模塊,該模塊執行與我願意執行的任務幾乎相同的任務。 它使用相同的方法進行簽名和輕微修改。 重寫我自己的MVC模塊肯定是重復代碼。 現有模塊本質上是針對其在軟件另一部分中的應用而開發的,我不能簡單地使用同一模塊。 但是寫成Model-View-Controller模式,其中Controller是Singleton。 我已經派生了View。

第二,我懷疑我能否經典地推導出Singleton類。

從繼承的類調用構造函數將只為父類調用getinstance(),而不能從派生類(?)返回對象。

第三,這就是我如何處理的方式。 請發表評論/幫助我改善!

我將整個Singleton類復制到可以稱為AbstractController的類中。 我兩次上這堂課。 第一個孩子是單身,並采用父母班級的全部待遇。 第二個孩子是我自己的應用程序控制器,具有自己的重新定義的處理方式。

謝謝!

事實是,單身人士和繼承者不能很好地合作。

是啊,是啊,辛格爾頓愛好者和GoF的崇拜將是對我的一切對於這一點,說:“好了,如果你把你的構造保護...”和“你不必擁有getInstance在類的方法,你可以說吧...”,但他們只是證明了我的意思。 單身人士必須跳很多圈,才能同時成為單身人士和基類。

但是,僅回答這個問題,就說我們有一個單例基類。 它甚至可以在某種程度上通過繼承來實現其單一性。 (構造函數執行了一些不再可以私有的工作時可以做的事情之一:如果另一個Base已經存在,它將引發異常。)說我們還有一個繼承自Base Derived類。 由於我們允許繼承,因此我們也可以說Base可以有任何其他子類,它們可以繼承自Derived ,也可以不繼承。

但是有一個問題-您已經遇到或即將遇到的那個問題。 如果我們在沒有構造對象的情況下調用Base::getInstance ,我們將得到一個空指針。 我們想找回任何存在的單例對象(它可能是Base和/或Derived和/或Other )。 但是這樣做仍然很困難,而且仍然要遵循所有規則,因為只有幾種方法可以這樣做-而且它們都有一些缺點。

  • 我們可以創建一個Base並返回它。 螺桿DerivedOther 最終結果: Base::getInstance()始終完全返回Base 子班永遠不會玩。 IMO,金達(Kinda)擊敗了目標。

  • 我們可以將自己的getInstance放在派生類中,並讓調用者說Derived::getInstance()如果他們特別想要Derived 這大大增加了耦合(因為調用者現在必須知道要專門請求Derived ,並且最終將自己綁定到該實現)。

  • 我們可以做最后一個的變體,但是該函數只是創建一個而不是獲取實例。 (雖然我們在處理它,但我們不將其重命名為initInstance ,因為我們並不特別關心它得到的內容-我們只是在調用它,以便它創建一個新的Derived並將其設置為One True Instance。)

因此(除非有任何未知的奇怪之處),它的工作原理如下:

class Base {
    static Base * theOneTrueInstance;

  public:
    static Base & getInstance() {
        if (!theOneTrueInstance) initInstance();
        return *theOneTrueInstance;
    }
    static void initInstance() { new Base; }

  protected:
    Base() {
         if (theOneTrueInstance) throw std::logic_error("Instance already exists");
         theOneTrueInstance = this;
    }

    virtual ~Base() { } // so random strangers can't delete me
};

Base* Base::theOneTrueInstance = 0;


class Derived : public Base {
  public:
    static void initInstance() {
        new Derived;  // Derived() calls Base(), which sets this as "the instance"
    }

  protected:
    Derived() { }   // so we can't be instantiated by outsiders
    ~Derived() { }  // so random strangers can't delete me
};

在初始化代碼中,您說的是Base::initInstance(); Derived::initInstance(); ,具體取決於您希望單身人士成為哪種類型。 當然,必須使用Base::getInstance()的返回值才能使用任何特定於Derived函數,但是,如果不進行強制轉換,則可以使用Base定義的任何函數,包括被Derived覆蓋的虛函數。

請注意,盡管如此,這種方式也有很多缺點:

  • 它將強制單一性的大部分負擔放在了基類上。 如果底座不具備此功能或類似功能,並且您無法更改它,那說明您已陷入困境。

  • 但是,基類不能承擔全部責任-每個類都需要聲明一個受保護的析構函數,否則有人可以在適當地強制轉換后實例化並刪除了一個實例,然后整個事情陷入了地獄。 更糟糕的是,編譯器無法強制執行此操作。

  • 因為我們使用受保護的析構函數來防止某些隨機的schmuck刪除我們的實例,除非編譯器比我所擔心的要聰明,否則即使運行時也無法在程序結束時正確刪除您的實例。 再見,RAII ...您好,“檢測到內存泄漏”警告。 (當然,任何合適的操作系統最終都會回收內存。但是,如果析構函數未運行,則無法依靠它為您進行清理。您需要先調用某種清理函數,然后才能進行此清理。退出,這將無法給您任何RAII可以給您的保證。)

  • 它公開了一個IMO並不真正屬於每個人都能看到的API的initInstance方法。 如果願意,可以將initInstance私有,並讓init函數成為friend ,但隨后您的類對外部代碼進行了假設,並且耦合作用重新發揮了作用。

另請注意,上面的代碼根本不是線程安全的。 如果需要,您可以自己決定。

認真地講,痛苦程度較小的途徑是忘記嘗試執行單一性。 確保只有一個實例的最簡單的方法是只創建一個。 如果需要在多個地方使用它,請考慮依賴注入。 (非框架版本相當於“將對象傳遞給需要它的東西”。:P)我去設計了上面的東西,只是試圖證明自己對單例和繼承是錯誤的,並向自己重申了邪惡是多么邪惡。組合是。 我不建議您在實際代碼中實際執行此操作。

我不確定我是否完全了解您正在處理的情況,是否有可能或適當地從單例中派生取決於很大程度上是如何實現單例的。

但是,由於您提到了“良好實踐”,因此在閱讀問題時會想到一些一般要點:

  1. 繼承通常不是實現代碼重用的最佳工具。 請參閱: 優先考慮組成而不是繼承?

  2. 使用單例和“良好實踐”通常不會在一起! 請參閱: 單身人士有什么不好?

希望能有所幫助。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM