1. <s id="4jtld"></s>
    1. <span id="4jtld"><meter id="4jtld"></meter></span>

        <span id="4jtld"></span>
      1. <s id="4jtld"><noscript id="4jtld"><i id="4jtld"></i></noscript></s>
        溫馨提示×

        c++控制對象創建方式和創建數量的方法

        發布時間:2020-08-15 09:22:02 來源:億速云 閱讀:201 作者:小新 欄目:開發技術

        小編給大家分享一下c++控制對象創建方式和創建數量的方法,相信大部分人都還不怎么了解,因此分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后大有收獲,下面讓我們一起去了解一下吧!

        我們知道,C++將內存劃分為三個邏輯區域:堆、棧和靜態存儲區。既然如此,我稱位于它們之中的對象分別為堆對象,棧對象以及靜態對象。通常情況下,對象創建在堆上還是在棧上,創建多少個,這都是沒有限制的。但是有時會遇到一些特殊需求。

        1.禁止創建棧對象

        禁止創建棧對象,意味著只能在堆上創建對象。創建棧對象時會移動棧頂指針以“挪出”適當大小的空間,然后在這個空間上直接調用類的構造函數以形成一個棧對象。而當棧對象生命周期結束,如棧對象所在函數返回時,會調用其析構函數釋放這個對象,然后再調整棧頂指針收回那塊棧內存。在這個過程中是不需要operator new/delete操作的,所以將operator new/delete設置為private不能達到目的。

        可以將構造函數或析構函數設為私有的,這樣系統就不能調用構造/析構函數了,當然就不能在棧中生成對象了。這樣的確可以,但有一點需要注意,那就是如果我們將構造函數設置為私有,那么我們也就不能用new來直接產生堆對象了,因為new在為對象分配空間后也會調用它的構造函數。所以,如果將構造函數和析構函數都聲明為private會帶來較大的副作用,最好的方法是將析構函數聲明為private,而構造函數保持為public。

        再進一步,將析構函數設為private除了會限制棧對象生成外,還有其它影響嗎?是的,這還會限制繼承。如果一個類不打算作為基類,通常采用的方案就是將其析構函數聲明為private。為了限制棧對象,卻不限制繼承,我們可以將析構函數聲明為protected,這樣就兩全其美了。如下代碼所示:

        class NoStackObject{ 
        protected: 
         ~NoStackObject(){} 
        public: 
         void destroy(){ 
          delete this ;//調用保護析構函數 
         } 
        };

        上面的類在創建棧對象時,如NoStackObject obj;時編譯將會報錯,而采用new的方式,編譯就會通過。需要注意一點的是,通過new創建堆對象時,在手動釋放對象內存時,我們需要調用其析構函數,這時就需要一點技巧來輔助——引入偽析構函數destory,如上面的代碼所示。

        方法拓展。 

        仔細一看,我們會發現上面的方法讓人別扭。我們用new創建一個對象,卻不是用delete去刪除它,而是要用destroy方法。很顯然,用戶會不習慣這種怪異的使用方式。所以,可以將構造函數也設為private或protected。這又回到了上面曾試圖避免的問題,即不用new,那么該用什么方式來生成一個對象了?我們可以用間接的辦法完成,即讓這個類提供一個static成員函數專門用于產生該類型的堆對象。(設計模式中的singleton模式就可以用這種方式實現。)讓我們來看看:

        class NoStackObject { 
        protected: 
         NoStackObject() { } 
         ~NoStackObject() { } 
        public: 
         static NoStackObject* creatInstance() {
         return new NoStackObject() ;//調用保護的構造函數 
        } 
         void destroy() {
          delete this ;//調用保護的析構函數 
         } 
        };

        現在可以這樣使用NoStackObject類了:

        NoStackObject* hash_ptr = NoStackObject::creatInstance() ; 
        ... ... //對hash_ptr指向的對象進行操作 
        hash_ptr->destroy() ; 
        hash_ptr = NULL ; //防止使用懸掛指針

        現在感覺是不是好多了,生成對象和釋放對象的操作一致了。

        2.禁止創建堆對象

        我們已經知道,產生堆對象的唯一方法是使用new操作,如果我們禁止使用new不就行了么。再進一步,new操作執行時會調用operator new,而operator new是可以重載的。方法有了,就是使new operator 為private,為了對稱,最好將operator delete也重載為private。

        class NoStackObject{
        private:
         static void* operator new(size_t size);
         static void operator delete(void* ptr);
        };
        
        //用戶代碼
        NoStackObject obj0;    //OK
        static NoStackObject obj1;  //OK
        NoStackObject * pObj2 = new NoStackObject; //ERROR

        如果也想禁止堆對象數組,可以把operator new[]和operator delete[]也聲明為private。

        這里同樣在繼承時存在問題,如果派生類改寫了operator new和operator delete并聲明為public,則基類中原有的private版本將失效,參考如下代碼:

        class NoStackObject{
        protected:
         static void* operator new(size_t size);
         static void operator delete(void* ptr);
        };
        
        
        class NoStackObjectSon:public NoStackObject{
        public:
         static void* operator new(size_t size){ //非嚴格實現,僅作示意之用 
          return malloc(size);
         };
         static void operator delete(void* ptr){ //非嚴格實現,僅作示意之用 
          free(ptr);
         };
        };
        
        //用戶代碼
        NoStackObjectSon* pObj2 = new NoStackObjectSon; //OK

        3.控制實例化對象的個數

        在游戲設計中,我們采用類CGameWorld作為游戲場景的抽象描述。然而在游戲運行過程中,游戲場景只有一個,也就是對CGameWorld對象的只有一個。對于對象的實例化,有一點是十分確定的:要調用構造函數。所以,如果想控制CGameWorld的實例化對象只有一個,最簡單的方法就是將構造函數聲明為private,同時提供一個static對象。如下:

        class CGameWorld
        {
        public:
         bool Init();
         void Run();
        private:
         CGameWorld();
         CGameWorld(const CGameWorld& rhs);
        
         friend CGameWorld& GetSingleGameWorld();
        };
        
        CGameWorld& GetSingleGameWorld()
        {
         static CGameWorld s_game_world;
         return s_game_world;
        }

        這個設計有三個要點: 

         (1)類的構造函數是private,阻止對象的建立;
         (2)GetSingleGameWorld函數被聲明為友元,避免了私有構造函數引起的限制;
         (3)s_game_world為一個靜態對象,對象唯一。

        當用到CGameWorld的唯一實例化對象時,可以如下:

        GetSingleGameWorld().Init();
        GetSingleGameWorld().Run();

        如果有人對GetSingleGameWorld是一個全局函數有些不爽,或者不想使用友元,將其聲明為類CGameWorld的靜態函數也可以達到目的,如下:

        class CGameWorld
        {
        public:
         bool Init();
         void Run();
         static CGameWorld& GetSingleGameWorld();
        private:
         CGameWorld();
         CGameWorld(const CGameWorld& rhs);
        };

        這就是設計模式中著名的單件模式:保證一個類僅有一個實例,并提供一個訪問它的全局訪問點。

        如果我們想讓對象產生的個數不是一個,而是最大為N(N>0)個??梢栽陬悆炔吭O置一個靜態計數變量,在調用構造函數時,該變量加1,當調用析構函數時,該變量減1。如下:

        class CObject
        {
        public:
         CObject();
         ~CObject();
        private:
         static size_t m_nObjCount;
         ...
        };
        
        CObject::CObject()
        {
         if (m_nObjCount > N)
          throw;
         m_nObjCount++;
        }
        
        CObject::~CObject()
        {
         m_nObjCount--;
        }
        size_t CObject::m_nObjCount;

        掌握控制類的實例化對象個數的方法。當實例化對象唯一時,采用設計模式中的單件模式;當實例化對象為N(N>0)個時,設置計數變量是一個思路。

        閱讀上面的示例代碼還需要注意拋出異常時沒有對象,即throw后沒有對象,有兩種含義: 

         (1)如果throw;在catch塊中或被catch塊調用的函數中出現,表示重新拋出異常。throw;表達式將重新拋出當前正在處理的異常。 我們建議采用該形式,因為這將保留原始異常的多態類型信息。重新引發的異常對象是原始異常對象,而不是副本。

        (2)如果throw;出現在非catch塊中,表示拋出不能被捕獲的異常,即使catch(…)也不能將其補捕獲。

        以上是c++控制對象創建方式和創建數量的方法的所有內容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內容對大家有所幫助,如果還想學習更多知識,歡迎關注億速云行業資訊頻道!

        免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

        主題地圖

        欧美午夜理伦三级在线观看,欧美午夜乱伦片,欧美午夜乱色视频在线观看,欧美午夜免费一区二区,欧美午夜片欧美片在线观看