• <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>

            清風(fēng)竹林

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

            基類角色之對(duì)象管理器

            基類角色之對(duì)象管理器

            版本:0.1

            最后修改:2009-07-02

            撰寫(xiě):李現(xiàn)民


            問(wèn)題描述

            C++程序設(shè)計(jì)中,保存一個(gè)生命周期不是由類對(duì)象自己維護(hù)的其它對(duì)象的指針通常是個(gè)壞主意,因?yàn)槌绦蜻壿嫼茈y判斷在使用該指針的時(shí)刻其所指對(duì)象是否已經(jīng)被銷毀。這種應(yīng)用需求很常見(jiàn),例如在網(wǎng)游設(shè)計(jì)中,由于華麗的裝備加載需要進(jìn)行大量硬盤(pán)I/O,因此加載過(guò)程通常由一個(gè)獨(dú)立的加載線程執(zhí)行,由于在裝備加載完成的時(shí)刻該玩家很可能已經(jīng)下線,因此加載線程就需要能夠去判斷此時(shí)玩家對(duì)象是否仍然有效。

            為了解決該問(wèn)題,通常會(huì)設(shè)計(jì)一個(gè)PlayerManager類用于跟蹤管理當(dāng)前所有的玩家對(duì)象,而加載線程通過(guò)提供玩家id以確認(rèn)該玩家對(duì)象仍然存在。此種設(shè)計(jì)方案需要一個(gè)獨(dú)立的PlayerManager類,并提供一個(gè)全局的PlayerManager類對(duì)象以跟蹤當(dāng)前的所有玩家對(duì)象。

            出于代碼復(fù)用的目的,我希望實(shí)現(xiàn)一個(gè)通用基類解決此類問(wèn)題。該基類需要為子類對(duì)象至少提供以下幾方面的能力:

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

            2. 自動(dòng)跟蹤類對(duì)象的生成與銷毀,不需要手工編寫(xiě)額外代碼;

            3. 實(shí)現(xiàn)迭代器,提供遍歷當(dāng)前所有有效對(duì)象的能力;

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

            5. 各子類實(shí)現(xiàn)擁有完全獨(dú)立的管理器邏輯;


            解決方案

            將實(shí)現(xiàn)代碼保存為objectman.hpp,內(nèi)容如下:

            /********************************************************************
            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};                            // 無(wú)效索引

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

                    
            private:
                        typename object_map::iterator _iter;
                    };

                
            public:
                    
            // 構(gòu)造函數(shù)
                    objectman(void)
                    {
                        enable_index();
                    }

                    
            // copy 構(gòu)造函數(shù)
                    objectman(const objectman& rhs)
                    {
                        enable_index();
                    }

                    
            // 析構(gòu)函數(shù)
                    virtual ~objectman(void)
                    {
                        disable_index();
                    }

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

                    }

                    
            // 通過(guò)索引獲取對(duì)象
                    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;
                    }

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

                    
            // 生成索引(使能被對(duì)象管理器遍歷到)
                    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)));
                    }
                    
                    
            // 移除索引(使不能被對(duì)象管理器遍歷到)
                    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;                          // 對(duì)象容器
                    static index_t        _idxGenderator;                    // 索引發(fā)生器

                    index_t                _idxObject;                      
            // 對(duì)象索引
                };

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

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

            #endif

            測(cè)試代碼

            測(cè)試代碼如下:

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

            };

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

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

                system(
            "pause");
                
            return 0
            }

            vs2008下所有斷言均動(dòng)作通過(guò)。


            已知問(wèn)題

            1. 在大量生成對(duì)象的情況下,index索引空間(代碼定義為int的范圍)有可能使用殆盡,甚至產(chǎn)生重復(fù),這會(huì)導(dǎo)致兩個(gè)對(duì)象擁有相同index的嚴(yán)重錯(cuò)誤;

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






            posted on 2009-07-02 12:49 李現(xiàn)民 閱讀(2012) 評(píng)論(11)  編輯 收藏 引用 所屬分類: design

            評(píng)論

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

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

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

            我一般是用一個(gè)ID管理器來(lái)解決這個(gè)問(wèn)題。
            采用直接索引的辦法。對(duì)于重復(fù)的ID使用一個(gè)順序增加的KEY來(lái)做驗(yàn)證。

            也就是一個(gè)ID,一個(gè)KEY來(lái)索引一個(gè)對(duì)象。

            之前還有一個(gè)方法,就是采用一種LINK對(duì)象,LINK對(duì)象能鏈接到另外一個(gè)LINK對(duì)象,并互換HOST指針,并且有主從關(guān)系的LINK方式。
            當(dāng)一個(gè)LINK對(duì)象銷毀的時(shí)候,會(huì)通知所有已經(jīng)連接的LINK對(duì)象放棄自己的HOST指針。這樣的話,別的對(duì)象里面保存的指針會(huì)隨著原始對(duì)象的銷毀而自動(dòng)清空。
              回復(fù)  更多評(píng)論   

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

            雖然不是很明白飯中淹關(guān)于LINK對(duì)象的意思,但是我獲得了靈感,寫(xiě)了下面的代碼:
            ///
            ///
            ///
            #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內(nèi)部會(huì)保存一個(gè)CMonster的指針,當(dāng)CMonster被刪除掉時(shí),會(huì)自動(dòng)更新CMonsterAI內(nèi)部的CMonster“指針”為NULL。接口比較簡(jiǎn)單:1)在會(huì)被引用(即被其他對(duì)象保存其指針)的類設(shè)計(jì)中派生(或組合)ref_base類,ref_base類簡(jiǎn)單來(lái)說(shuō)就是保存一個(gè)引用類對(duì)象列表,通過(guò)基類ref_op可以使得ref_base保存不同類型的引用類(例如CMonsterAI可以保存CMonster, CRegion也可以保存CMonster),ref_base在析構(gòu)時(shí)自動(dòng)回調(diào)引用類對(duì)象的be_null函數(shù),be_null函數(shù)在auto_null類中自動(dòng)把CMonster*設(shè)置為NULL。
            通過(guò)重載operator=,使得SetOwner函數(shù)里不需要做其他操作(看起來(lái))。
              回復(fù)  更多評(píng)論   

            # re: 基類角色之對(duì)象管理器 2009-07-02 18:02 李現(xiàn)民

            @飯中淹
            對(duì)于“也就是一個(gè)ID,一個(gè)KEY來(lái)索引一個(gè)對(duì)象”這種辦法,我想可以通過(guò)增加ID(我我的實(shí)現(xiàn)代碼是是index_t)的長(zhǎng)度來(lái)代替,即定義:
            typedef __int64 index_t; // 索引類型

            我認(rèn)為效果是等價(jià)的。

              回復(fù)  更多評(píng)論   

            # re: 基類角色之對(duì)象管理器 2009-07-02 18:05 李現(xiàn)民

            @Kevin Lynx
            我感覺(jué)那個(gè)link的意思有點(diǎn)像觀察者observer,即:當(dāng)一個(gè)對(duì)象銷毀時(shí)通知所有對(duì)其感興趣的對(duì)象,告訴它們:我死了,不要再來(lái)找我

            但這種方式,我個(gè)人覺(jué)得有點(diǎn)復(fù)雜了  回復(fù)  更多評(píng)論   

            # re: 基類角色之對(duì)象管理器[未登錄](méi) 2009-07-02 21:51 塵埃

            id用兩個(gè)部分組合而成:數(shù)組索引、累加,64位或許應(yīng)該夠了,且短時(shí)間內(nèi)很難發(fā)生碰撞吧。數(shù)組索引用來(lái)立即找出對(duì)應(yīng)對(duì)象,再用累加部分驗(yàn)證一下,實(shí)在不放心把累加部分?jǐn)U大到64位,對(duì)于客戶端來(lái)說(shuō)怎么都該夠了;)  回復(fù)  更多評(píng)論   

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

            @Kevin Lynx
            兄弟這段代碼很經(jīng)典啊,學(xué)習(xí)了!   回復(fù)  更多評(píng)論   

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

            @Kevin Lynx
            跟這個(gè)差不多。不過(guò)不用繼承

            xLink<Host, Target>


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

            *
            &
            是重載的。
            返回Host
              回復(fù)  更多評(píng)論   

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

            @李現(xiàn)民
            確實(shí)是類似觀察者這種,不過(guò)更直接了
              回復(fù)  更多評(píng)論   

            # re: 基類角色之對(duì)象管理器 2009-07-03 12:03 李現(xiàn)民

            @Kevin Lynx
            今天我看了下,你這種實(shí)現(xiàn)方式就已經(jīng)是觀察者了,其中CMonster 就是被觀察的(observable),而所有使用auto_null<>對(duì)象的都是觀察者(observer), 這種實(shí)現(xiàn)方法我覺(jué)得至少有兩個(gè)缺點(diǎn):
            1,會(huì)強(qiáng)制observable都使用ref_base 基類,同時(shí)observer需要使用auto_null<>去封裝對(duì)象,因此即使新設(shè)計(jì)的類可以使用auto_null,但舊有的、需要被觀察的類無(wú)法自動(dòng)成為observable,因?yàn)樗鼈儾辉^承ref_base
            2,使用auto_null對(duì)象時(shí),IDE的智能通常無(wú)法診測(cè)出具體對(duì)象(CMonster)內(nèi)部的函數(shù)名,只能診測(cè)出像fetch()這些  回復(fù)  更多評(píng)論   

            # re: 基類角色之對(duì)象管理器 2009-07-04 16:29 99網(wǎng)上書(shū)店

            確實(shí)是類似觀察者這種,不過(guò)更直接了  回復(fù)  更多評(píng)論   

            久久久久久久久久久久久久| 久久精品蜜芽亚洲国产AV| 性做久久久久久久久久久| 囯产极品美女高潮无套久久久| 国产精品99久久免费观看| 久久午夜福利电影| 日本强好片久久久久久AAA | 亚洲午夜久久久| 国内精品久久久久影院一蜜桃 | 亚洲国产日韩欧美综合久久| 色诱久久久久综合网ywww | 亚洲国产精品无码久久久不卡 | 久久久久久夜精品精品免费啦| 精品综合久久久久久88小说| 97久久国产露脸精品国产| 久久国产高清一区二区三区| 国产∨亚洲V天堂无码久久久| 久久亚洲AV无码精品色午夜| 久久精品无码专区免费 | 色8激情欧美成人久久综合电| 99久久婷婷国产综合亚洲| 欧美黑人激情性久久| 久久99亚洲综合精品首页| 久久精品aⅴ无码中文字字幕重口| 午夜精品久久久久久久无码| 久久本道综合久久伊人| 91精品国产高清久久久久久国产嫩草| 婷婷久久香蕉五月综合加勒比| 久久99热这里只有精品66| 久久精品三级视频| 国产午夜福利精品久久| 久久久精品午夜免费不卡| 国产人久久人人人人爽 | 久久精品免费一区二区| 亚洲精品视频久久久| 亚洲精品99久久久久中文字幕 | 久久99国产精品一区二区| 1000部精品久久久久久久久| 日韩精品久久久久久久电影蜜臀| 精品国产乱码久久久久软件| 国内精品久久久久久久久电影网 |