• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            清風竹林

            ぷ雪飄絳梅映殘紅
               ぷ花舞霜飛映蒼松
                 ----- Do more,suffer less

            基類角色之對象管理器

            基類角色之對象管理器

            版本:0.1

            最后修改:2009-07-02

            撰寫:李現民


            問題描述

            C++程序設計中,保存一個生命周期不是由類對象自己維護的其它對象的指針通常是個壞主意,因為程序邏輯很難判斷在使用該指針的時刻其所指對象是否已經被銷毀。這種應用需求很常見,例如在網游設計中,由于華麗的裝備加載需要進行大量硬盤I/O,因此加載過程通常由一個獨立的加載線程執行,由于在裝備加載完成的時刻該玩家很可能已經下線,因此加載線程就需要能夠去判斷此時玩家對象是否仍然有效。

            為了解決該問題,通常會設計一個PlayerManager類用于跟蹤管理當前所有的玩家對象,而加載線程通過提供玩家id以確認該玩家對象仍然存在。此種設計方案需要一個獨立的PlayerManager類,并提供一個全局的PlayerManager類對象以跟蹤當前的所有玩家對象。

            出于代碼復用的目的,我希望實現一個通用基類解決此類問題。該基類需要為子類對象至少提供以下幾方面的能力:

            1. 為所有的對象分配一個全局唯一的index,通過該index能夠(盡可能快的)獲取到擁有該index的類對象(或NULL);

            2. 自動跟蹤類對象的生成與銷毀,不需要手工編寫額外代碼;

            3. 實現迭代器,提供遍歷當前所有有效對象的能力;

            4. 提供“移除”接口,使得對象可以主動要求放棄被對象管理器跟蹤;

            5. 各子類實現擁有完全獨立的管理器邏輯;


            解決方案

            將實現代碼保存為objectman.hpp,內容如下:

            /********************************************************************
            created:    2009-06-29
            author:    lixianmin

            purpose:    base class for object manager
            Copyright (C) 2009 - All Rights Reserved
            ********************************************************************
            */
            #ifndef _LIB_OBJECT_MAN_HPP_INCLUDED_
            #define _LIB_OBJECT_MAN_HPP_INCLUDED_
            #include 
            <cassert>
            #include 
            <map>

            namespace lib
            {
                template
            <typename T>
                
            class objectman
                {
                
            public:
                    typedef 
            int index_t;                                // 索引類型
                    typedef std::map<index_t, T*>   object_map;         // 容器類型

                    
            enum { INVAID_INDEX= 0};                            // 無效索引

                
            public:
                    
            // 迭代器
                    class iterator
                    {
                    
            public:
                        iterator(
            void): _iter(_mObjects.begin()){}                          // 構造函數
                        bool has_next(voidconst { return (_mObjects.end()!= _iter); }     // 測試是否還有下一個對象
                        T* next(void) { return has_next()? (_iter++->second): NULL;    }    // 獲取下一個對象指針

                    
            private:
                        typename object_map::iterator _iter;
                    };

                
            public:
                    
            // 構造函數
                    objectman(void)
                    {
                        enable_index();
                    }

                    
            // copy 構造函數
                    objectman(const objectman& rhs)
                    {
                        enable_index();
                    }

                    
            // 析構函數
                    virtual ~objectman(void)
                    {
                        disable_index();
                    }

                    
            // 賦值操作符
                    objectman& operator= (const objectman& rhs)
                    {

                    }

                    
            // 通過索引獲取對象
                    static T* get_by_index(index_t index)
                    {
                        object_map::iterator iter
            = _mObjects.find(index);
                        
            if (_mObjects.end()!= iter)
                        {
                            T
            * pObject= iter->second;
                            assert(NULL
            != pObject);
                            
            return pObject;
                        }

                        
            return NULL;
                    }

                    
            // 獲取對象索引
                    index_t get_index(voidconst { return _idxObject; }

                    
            // 生成索引(使能被對象管理器遍歷到)
                    void enable_index(void)
                    {
                        _idxObject
            = ++_idxGenderator;
                        assert(get_index()
            != INVAID_INDEX);
                        assert(_mObjects.find(get_index())
            == _mObjects.end());
                        _mObjects.insert(std::make_pair(get_index(), static_cast
            <T*>(this)));
                    }
                    
                    
            // 移除索引(使不能被對象管理器遍歷到)
                    void disable_index(void)
                    {
                        
            if (get_index()!= INVAID_INDEX)
                        {
                            assert(_mObjects.find(get_index())
            != _mObjects.end());
                            _mObjects.erase(get_index());

                            _idxObject
            = INVAID_INDEX;
                        }
                    }

                
            private:
                    friend 
            class iterator;
                    
            static object_map    _mObjects;                          // 對象容器
                    static index_t        _idxGenderator;                    // 索引發生器

                    index_t                _idxObject;                      
            // 對象索引
                };

                template
            <typename T>
                typename objectman
            <T>::object_map objectman<T>::_mObjects;

                template
            <typename T>
                typename objectman
            <T>::index_t objectman<T>::_idxGenderator= 0;
            }

            #endif

            測試代碼

            測試代碼如下:

            #include <cassert>
            #include 
            "objectman.hpp"
            // 聲明一個類
            class Player:public lib::objectman<Player>
            {

            };

            int main(int argc, char* argv[])
            {
                
            const int idxDisabled= 5;
                
            // 生成對象
                for (int i= 0; i< 10++i)
                {
                    Player
            * pPlayer= new Player;
                    
            if (idxDisabled== pPlayer->get_index())
                    {
                        
            // 從對象管理器中移除該對象
                        pPlayer->disable_index();
                    }
                }
                
                
            //使用迭代器遍歷類對象
                Player::iterator iter;
                
            while(iter.has_next())
                {
                    Player
            * pPlayer= iter.next();
                    
            const int idxPlayer= pPlayer->get_index();
                    
            // 斷言之:遍歷不到已經移除的對象
                    assert(idxPlayer!= idxDisabled);
                    
            // 斷言之:可以通過idxPerson取得對象指針
                    assert(pPlayer== Player::get_by_index(idxPlayer));
                    
                    
            // 回收對象
                    delete pPlayer;
                    pPlayer
            = NULL;
                }

                
            // 斷言之:所有對象均已被刪除
                Player::iterator iter2;
                assert(
            !iter2.has_next());

                system(
            "pause");
                
            return 0
            }

            vs2008下所有斷言均動作通過。


            已知問題

            1. 在大量生成對象的情況下,index索引空間(代碼定義為int的范圍)有可能使用殆盡,甚至產生重復,這會導致兩個對象擁有相同index的嚴重錯誤;

            2. std::map的查找速度不是特別另人滿意;






            posted on 2009-07-02 12:49 李現民 閱讀(2010) 評論(11)  編輯 收藏 引用 所屬分類: design

            評論

            # re: 基類角色之對象管理器 2009-07-02 14:10 Kevin Lynx

            本質上就是通過讓其他模塊不保存這個object的直接指針,而是一個ID,然后通過一個manager由這個ID而獲取到這個object*,是可以有效減少指針無效的問題。但是,面對的最大問題就在于這個查找過程。你這里用的是std::map,我們用的是stdext::hash_map,我自己都覺得有點速度問題。希望能在這個問題上找到良好的解決辦法。:)  回復  更多評論   

            # re: 基類角色之對象管理器 2009-07-02 15:20 飯中淹

            我一般是用一個ID管理器來解決這個問題。
            采用直接索引的辦法。對于重復的ID使用一個順序增加的KEY來做驗證。

            也就是一個ID,一個KEY來索引一個對象。

            之前還有一個方法,就是采用一種LINK對象,LINK對象能鏈接到另外一個LINK對象,并互換HOST指針,并且有主從關系的LINK方式。
            當一個LINK對象銷毀的時候,會通知所有已經連接的LINK對象放棄自己的HOST指針。這樣的話,別的對象里面保存的指針會隨著原始對象的銷毀而自動清空。
              回復  更多評論   

            # re: 基類角色之對象管理器 2009-07-02 17:24 Kevin Lynx

            雖然不是很明白飯中淹關于LINK對象的意思,但是我獲得了靈感,寫了下面的代碼:
            ///
            ///
            ///
            #include <stdio.h>
            #include <list>

            class ref_op
            {
            public:
            virtual void be_null() { }
            };

            typedef std::list<ref_op*> RefOpList;
            class ref_base
            {
            public:
            ~ref_base()
            {
            clear_ops();
            }

            void add_ref( ref_op *op )
            {
            _oplist.push_back( op );
            }

            void clear_ops()
            {
            for( RefOpList::const_iterator it = _oplist.begin();
            it != _oplist.end(); ++ it )
            {
            (*it)->be_null();
            }
            }

            private:
            RefOpList _oplist;
            };

            template <typename _Tp>
            class auto_null : public ref_op
            {
            public:
            void fetch( _Tp *t )
            {
            _t = t;
            t->add_ref( this );
            }
            auto_null<_Tp> &operator = ( _Tp *t )
            {
            fetch( t );
            return *this;
            }
            void be_null()
            {
            _t = 0;
            }
            operator _Tp*()
            {
            return _t;
            }
            private:
            _Tp *_t;
            };

            //////////////////////////////////////////////////////////////////////////////
            class CMonster : public ref_base
            {
            };

            class CMonsterAI
            {
            public:
            void SetOwner( CMonster *pOwner )
            {
            m_Owner = pOwner;
            }

            void Test()
            {
            if( (CMonster*)m_Owner == NULL )
            {
            printf( "The owner is null.\n" );
            }
            else
            {
            printf( "The owner is NOT null.\n" );
            }
            }
            private:
            auto_null<CMonster> m_Owner;
            };

            int main()
            {
            CMonster *pMonster = new CMonster();
            CMonsterAI *pAI = new CMonsterAI();
            pAI->SetOwner( pMonster );
            pAI->Test();
            delete pMonster;
            pAI->Test();
            delete pAI;
            return 0;
            }

            CMonster內部會保存一個CMonster的指針,當CMonster被刪除掉時,會自動更新CMonsterAI內部的CMonster“指針”為NULL。接口比較簡單:1)在會被引用(即被其他對象保存其指針)的類設計中派生(或組合)ref_base類,ref_base類簡單來說就是保存一個引用類對象列表,通過基類ref_op可以使得ref_base保存不同類型的引用類(例如CMonsterAI可以保存CMonster, CRegion也可以保存CMonster),ref_base在析構時自動回調引用類對象的be_null函數,be_null函數在auto_null類中自動把CMonster*設置為NULL。
            通過重載operator=,使得SetOwner函數里不需要做其他操作(看起來)。
              回復  更多評論   

            # re: 基類角色之對象管理器 2009-07-02 18:02 李現民

            @飯中淹
            對于“也就是一個ID,一個KEY來索引一個對象”這種辦法,我想可以通過增加ID(我我的實現代碼是是index_t)的長度來代替,即定義:
            typedef __int64 index_t; // 索引類型

            我認為效果是等價的。

              回復  更多評論   

            # re: 基類角色之對象管理器 2009-07-02 18:05 李現民

            @Kevin Lynx
            我感覺那個link的意思有點像觀察者observer,即:當一個對象銷毀時通知所有對其感興趣的對象,告訴它們:我死了,不要再來找我

            但這種方式,我個人覺得有點復雜了  回復  更多評論   

            # re: 基類角色之對象管理器[未登錄] 2009-07-02 21:51 塵埃

            id用兩個部分組合而成:數組索引、累加,64位或許應該夠了,且短時間內很難發生碰撞吧。數組索引用來立即找出對應對象,再用累加部分驗證一下,實在不放心把累加部分擴大到64位,對于客戶端來說怎么都該夠了;)  回復  更多評論   

            # re: 基類角色之對象管理器 2009-07-03 09:55 zuhd

            @Kevin Lynx
            兄弟這段代碼很經典啊,學習了!   回復  更多評論   

            # re: 基類角色之對象管理器 2009-07-03 10:16 飯中淹

            @Kevin Lynx
            跟這個差不多。不過不用繼承

            xLink<Host, Target>


            xLink.link( xLink<Target, Host> & lnk );
            xLink.disLinkAll();

            *
            &
            是重載的。
            返回Host
              回復  更多評論   

            # re: 基類角色之對象管理器 2009-07-03 10:18 飯中淹

            @李現民
            確實是類似觀察者這種,不過更直接了
              回復  更多評論   

            # re: 基類角色之對象管理器 2009-07-03 12:03 李現民

            @Kevin Lynx
            今天我看了下,你這種實現方式就已經是觀察者了,其中CMonster 就是被觀察的(observable),而所有使用auto_null<>對象的都是觀察者(observer), 這種實現方法我覺得至少有兩個缺點:
            1,會強制observable都使用ref_base 基類,同時observer需要使用auto_null<>去封裝對象,因此即使新設計的類可以使用auto_null,但舊有的、需要被觀察的類無法自動成為observable,因為它們不曾繼承ref_base
            2,使用auto_null對象時,IDE的智能通常無法診測出具體對象(CMonster)內部的函數名,只能診測出像fetch()這些  回復  更多評論   

            # re: 基類角色之對象管理器 2009-07-04 16:29 99網上書店

            確實是類似觀察者這種,不過更直接了  回復  更多評論   

            亚洲人成无码www久久久| 日本欧美国产精品第一页久久| 午夜视频久久久久一区| 热99RE久久精品这里都是精品免费 | 女人香蕉久久**毛片精品| 久久夜色tv网站| 久久久国产99久久国产一| .精品久久久麻豆国产精品 | 久久精品免费一区二区三区| 国产精品无码久久久久 | 亚洲AV无码久久精品蜜桃| 成人免费网站久久久| 模特私拍国产精品久久| 亚洲国产二区三区久久| 亚洲精品乱码久久久久久蜜桃不卡 | 精品少妇人妻av无码久久| 国产三级精品久久| 久久99精品国产自在现线小黄鸭 | 久久久久久亚洲精品无码| 精品久久久噜噜噜久久久 | 精品久久人妻av中文字幕| 久久天天躁狠狠躁夜夜不卡 | 热RE99久久精品国产66热| 久久棈精品久久久久久噜噜| 久久国产亚洲精品| 欧美激情精品久久久久久久| 精品久久久无码中文字幕天天| 久久久国产乱子伦精品作者| 久久夜色精品国产噜噜亚洲a| 日本道色综合久久影院| 久久不射电影网| 国产精品岛国久久久久| 久久久久久久久久久久中文字幕 | 久久久久久久综合日本亚洲 | 精品一二三区久久aaa片| 亚洲国产成人久久笫一页| 久久99精品久久久久久齐齐| 激情久久久久久久久久| 国产成人久久久精品二区三区 | 久久精品aⅴ无码中文字字幕不卡| 欧美久久天天综合香蕉伊|