【深入瞭解cocos2d-x 3.x】內置數據結構(2)——Map

其實最沒意思的數據結構就是Map和Vector這兩個了,完完全全就是std::map和std::vector上面再加了一層引用計數。當然,這也有好處,就是支持std算法以及支持cocos2d-x的內存管理機制。

看源碼可以知道(下均只對Map進行分析,Vector同理)

template <class K, class V>
class Map
{
public:
    // ------------------------------------------
    // Iterators
    // ------------------------------------------
#if USE_STD_UNORDERED_MAP
    typedef std::unordered_map<K, V> RefMap;
#else
    typedef std::map<K, V> RefMap;
#endif
protected:
    RefMap _data;
};

內部的數據結構就是一個std::unordered_map,但是cocos2d-x對其做了一個限制,V必須是由Ref派生出來的數據類型,這樣才能支持內存管理機制,下面是構造函數(有多個構造函數,只列舉了一個)

    /** Default constructor */
    Map<K, V>()
    : _data()
    {
        static_assert(std::is_convertible<V, Ref*>::value, "Invalid Type for cocos2d::Map<K, V>!");
        CCLOGINFO("In the default constructor of Map!");
    }

static_assert是表示在編譯時檢查,std::is_convertible是檢測V與Ref*是否是繼承關係,如果是,value爲true,檢測通過。如果不是,會在編譯期就告訴我們,這個地方編譯不過。

接下來就是插入函數

    /** @brief Inserts new elements in the map.
     *  @note If the container has already contained the key, this function will erase the old pair(key, object)  and insert the new pair.
     *  @param key The key to be inserted.
     *  @param object The object to be inserted.
     */
    void insert(const K& key, V object)
    {
        CCASSERT(object != nullptr, "Object is nullptr!");
        erase(key);
        _data.insert(std::make_pair(key, object));
        object->retain();
    }

值得注意的是,這裏先有一個刪除操作,再進行插入,這樣爲了保證所有的key都是唯一值,但是這樣會多造成一次遍歷,因爲unordered_map的插入是無序的,所以unordered_map的insert操作的複雜度是O(1),但是erase的刪除必須要find key,所以會有O(N)的複雜度,所以效率會比直接使用unordered_map低。

然後是retain,保持對V類型的object的強引用

接下來是刪除函數

    /** @brief Removes an element with an iterator from the Map<K, V> container.
     *  @param k Key of the element to be erased.
     *         Member type 'K' is the type of the keys for the elements in the container,
     *         defined in Map<K, V> as an alias of its first template parameter (Key).
     */
    size_t erase(const K& k)
    {
        auto iter = _data.find(k);
        if (iter != _data.end())
        {
            iter->second->release();
            _data.erase(iter);
            return 1;
        }
        
        return 0;
    }
首先find到K,然後先執行release,再刪除,返回1表示刪除成功,0表示刪除失敗。

還有一些有意思的函數,如下這個

    V getRandomObject() const
    {
        if (!_data.empty())
        {
            ssize_t randIdx = rand() % _data.size();
            const_iterator randIter = _data.begin();
            std::advance(randIter , randIdx);
            return randIter->second;
        }
        return nullptr;
    }
函數的作用是返回一個隨機對象,首先判斷非空,然後獲取一個(0-data.size)的隨機數,使用std::advance給begin增加一個隨機數的長度,返回這個長度的迭代器。




發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章