How to design a class that can't be inherited(C++)

封裝性,繼承性和多態是面向對象的三大特徵,最近思考了一下,怎麼設計一個不能繼承性的類呢?

      C++要實現一個不能被繼承的類有不少方法.使用友元、私有構造函數、虛繼承等方式可使一個類不能被繼承,但是爲何必須是虛繼承?背後的原理又是什麼?ios

~的構造函數設置爲私有的就okay。面試

由於那樣的話,子類就沒有辦法訪問基類的構造函數,從而就阻止了進行子類構造對象的任務實現,也就達到了不可繼承的目的。函數

可是,假設那樣,這個類咱們在其它地方怎麼使用呢?那這樣子給咱們的利用也形成了必定的障礙。測試

好了。你是否是也想到了,定義靜態方法,在方法內部實現一個對象,而後返回它的指針。spa

Ok?那怎麼釋放掉呢?再照樣設計一個釋放內存函數,問題就會迎刃而解。設計

OK。按照這個邏輯分析。示例代碼以下:
指針

點擊(此處)摺疊或打開code

  1. #include<iostream>
  2.  using namespace std;
  3.   class A
  4.   {
  5.   public:
  6.      static A * Construct(int n)
  7.       {
  8.           A *pa = new A;
  9.           pa->num= n;
  10.           cout<<"num is:"<<pa->num<<endl;
  11.           return pa;
  12.      }
  13.      static void Destruct(A * pIntance)
  14.      {
  15.          delete pIntance;
  16.          pIntance = NULL;
  17.      }
  18.      private:
  19.              A(){}
  20.              ~A(){}
  21.      public:
  22.              int num;
  23.  };
  24.  void main()
  25.  {
  26.      A *f = A::Construct(9);
  27.      cout<<f->num<<endl;
  28.      A::Destruct(f);
  29.  }

好了,這個類就這樣子。按照咱們的理論分析,咱們的實踐結果是徹底成立的。對象

可是,這個題,它比較有挑戰性,什麼意思呢?難道你沒有發現,我們這水平也就僅僅有面試資格,還不能夠破格錄用的。繼承

怎麼啦?你可能會反問我。難道你真的沒有看明白?肯定沒有看明白?若是是真的話,那我就告訴你吧!

我們的類不能夠實如今棧上建立對象。也就是說,僅僅只能夠在堆上構建任何的一個對象,而在棧上就無能爲力了。

私有的構造函數極大的侷限性就這樣盡收眼底了。

好吧!咱們修改它,也就是所謂的爲它打「補丁吧」,請看示例代碼:

點擊(此處)摺疊或打開

#include<iostream>

 using namespace std;

template <typename T>

class Base

 {

friend T;

private:

  Base(){}

~Base(){}

 };

class Finalclass : virtual public Base<Finalclass>

{

 public:

Finalclass(){}

~Finalclass(){}

};

void main()

{

 Finalclass *p = new Finalclass; //堆上對象

 Finalclass fs; //棧上對象

 }

    OK。如今看看咱們的Finalclass類。

    繼承於Base,Base爲虛基類,由於它是Base的友元,因此,它能夠訪問基類的私有構造函數,以及析構函數。編譯運行時是正確的。

    也就是說,能夠建立堆上的對象,而且能夠構建棧上的對象。

    能否繼承?假如它做爲一個基類被一個類繼承,在編譯時是徹底能夠經過的。

    這一點沒有什麼疑問,問題就出在運行時:

    當子類在構造對象時,由於是虛繼承,因此子類的構造函數會直接去調用Base類的構造函數,而Base的構造函數是私有的。運行錯誤error!!!

    這就是一個真正不能被繼承的類。

    思路二:主要的思路就是使子類不能構造父類的部分,這樣子類就沒有辦法實例化整個子類.這樣就限制了子類的繼承. 因此咱們能夠將父類的構造函數聲明成爲私有的,可是這樣父類不就不能實例化,繼續思考、、、
      
    咱們能夠利用友員不能被繼承的特性!
       首先假設CParent不可以被繼承. 讓CParent是某一個類的友員和子類,CParent能夠構造,可是CParent的子類 CChild卻不能繼承那個友員特性,因此不能被構造.因此咱們引入一個CFinalClassMixin.
     
     咱們對這個類的功能是這麼指望的:
      任何類從它繼承都不能被實例化,同時這個類自己咱們也不但願它被實例化.
      實現一個構造函數和析構函數都是private的類就好了.同時在這類裏面將咱們的CParent聲明爲友員. 代碼以下:
      class CFinalClassMixin
      {
      friend class CParent;
      private:
       CFinalClassMixin(){}
      ~CFinalClassMixin(){}
      };
      //咱們的 CParent代碼應該以下:
      class CParent
      {
      public:
       CParent(){}
      ~CParent(){}
      };
      這個類(注,此時它仍是可以被繼承),如今咱們須要它不能被繼承.那麼只要將代碼改爲
      class CParent:public CFinalClassMixin
      {
      public:
      CParent(){}
      ~CParent(){}
      };
      就好了.如今從CParent繼承一個子類試試
      class CChild:public CParent{};
      編譯一下代碼試試,發現:居然沒有做用!!
      如今再回想一下咱們這麼操做的緣由,也就是這個方案的原理,那就是讓父類能夠訪問Mixin類的構造函數,可是子類不能訪問.
      如今看看咱們的代碼,發現父類是CFinalClassMixin類的友員,能夠訪問它的構造函數.由於友員不能繼承,因此CChild不能訪問CFinalClassMixin的構造函數.因此應該不能被實例化.
      CChild的確不能訪問 CFinalClassMixin的構造函數,可是它卻沒必要調用它!我想這就是問題的緣由所在.CChild是經過CParent來構造 CFinalClassMixin的,因此這個友員對他並無什麼用處!
      如今問題找到了.要解決很簡單.只要讓CChild必須調用 CFinalClassMixin的構造函數就好了,怎麼才能達到目的呢?
      還記得虛繼承嗎?虛繼承的一個特徵就是虛基類的構造函數由最終子類負責構造!因此將CParent從CFinalClassMixin繼承改爲從CFinalClassMixin虛繼承就能夠了.代碼以下:
      class CParent:virtual public CFinalClassMixin
      {
       public:
      CParent(){}
      CParent(){}
      };
      如今試試,行了.
      可是可能有些人會對多繼承心有餘悸!可是咱們這裏並無必要這麼擔憂!爲何?由於咱們的CFinalClassMixin類是純的!pure! 也就是說它根本沒有成員變量!那麼咱們就根本不用擔憂多繼承帶來的最大問題.菱形繼承產生的數據冗餘.以及二義性.
      如今還有個不足!那就是 咱們不能每次使用這個CFinalClassMixin類就在裏面加上對某個類的友員聲明啊!這多麻煩啊!雖然不是什麼大問題,可是我覺的仍是要解決,由於我充分信任C++!
      解決的方法也很簡單!那就是使用模板!具體描述就省略了,給出代碼你們一看就知道了
      下面是個人測試程序的完整代碼(其中的CFinalClassmixin已經改爲模板)

    #include "stdafx.h"
    #include <iostream>
    using namespace std;

    template<class T>   //應用模板
    class CFinalClassMixin
    {
     friend T;

     private:
     CFinalClassMixin(){}
        ~CFinalClassMixin(){}
    };

      
    class CParent:virtual public CFinalClassMixin<CParent>  //虛繼承
    {
     public:
       CParent(){}
       ~CParent(){}
    };

    class CChild:public CParent{}; //子類繼承父類

    int main(int argc, char* argv[])
    {
     CParent a; // 能夠構造
     CChild b; //不能構造
     return 0;
    }

    如今只要對不想被繼承的類加入一個CFinalClassMixin混合類作父類就好了.
      經過限制構造函數,咱們就達到了限制繼承的目的 .可是這對有些仍是個例外,好比全是靜態函數的類.這些類自己就不須要構造. 因此咱們對它沒有辦法.可是在大多數狀況下,一個全是靜態函數的類多少暗示了程序自己的設計多是須要斟酌的.
       其實這只是Mixin類(混合類)使用的一個小小例子.還有不少其餘的用處,好比UnCopiale等等.就很少說了. 我想說明的是你們可能對多繼承比較反感.可是過度否認也是得不償失的.如今對多繼承到底應不該該使用還處在爭論階段. 我以爲一個方法是否使用得當,關鍵仍是在於使用的人.



相關文章
相關標籤/搜索