Vector和ValueVector

魚與熊掌不可兼得,類似這種例子生活中會經常碰到。同樣的,如果你有去了解過Cocos2d-x 3.0,也會遇到這樣一個令人糾結的情況 -- Value 與 Vector(Map)。






爲什麼這麼說呢?且聽我慢慢道來。






在Cocos2d-x 2.0版本,我們要存儲一個int型數據,應該放到哪裏?沒錯,放到CCArray中,如下:


1
2
3
int i = 10;  
CCArray _array = CCArray::create();//創建一個CCArray數組  
_array->addObject(CCInteger::create(i));//將int型數據放入數組中




如果要存儲一個CCObject對象,又是用什麼存呢?是的,又是CCArray:


1
2
3
CCSprite* sp = CCSprite::create("star.png");//創建一個精靈  
...  
_array->addObject(sp);//將精靈放入到數組中




在Cocos2d-x3.0版本,我們都知道該版本的CCArray已經被甩了(實際上用__Array也還是可以替用一下),那麼要存儲一個Ref(3.0後CCObject改名爲Ref)對象應該如何操作?機智的我馬上想到了CCArray的替代者:Vector,示例代碼如下:


1
2
3
4
auto sp = Sprite::create("star.png");  
...  
Vector<Sprite*> sp_vec;//創建一個Sprite*類型的容器  
sp_vec.pushBack(sp);//將精靈放入到容器中




接下來問題來了,如果要存儲一個數據類型,如int型數據,那麼用Vector可以實現嗎?答案是否定的,在Vector的官方說明文檔裏有這麼一句話:


cocos2d::Vector<T> 中的T必須是一個指向cocos2d::Ref子類對象的指針。不能是其他數據類型或者原生類型,因爲我們已經將 Cocos2d-x 的內存管理模型集成到 cocos2d::Vector<T> 中。






既然Vector容不下數據類型的元素,那麼肯定有可以替代它的東西存在。沒錯,ValueVector登上了歷史舞臺。






先到CCValue.h頭文件中看下它的聲明:


1
typedef std::vector<Value> ValueVector;




可以看出,ValueVector實際上就是一個存放Value類型元素的std::vector容器,這裏和我之前的猜測有些出入。下面將幾個int型數據存儲到ValueVector中。


1
2
3
4
5
int a = 10;  
int b = 20;  
ValueVector val_vec;  
val_vec.push_back(Value(a));  
val_vec.push_back(Value(b));




上面代碼就是創建兩個int型的變量,然後放入ValueVector中,其中要注意的是:因爲ValueVector中只能存放Value類型的元素,所以int型的a、b變量必須轉換成Value類型後才能放入到ValueVector中。






說到ValueVector,那就順便提下它的一些簡單操作:


1、讀取Plist(xml)配置文件。如下:


1
ValueVector star_val = FileUtils::getInstance()->getValueVectorFromFile("star.plist");




不過用ValueVector讀取的plist文件只侷限於是該plist的格式的以array數組類型開頭的,例如下面這種:


1
2
3
4
5
6
7
8
<array>  
    <dict>  
        <key>name</key>  
        <string>star</string>  
        <key>isCool</key>  
        <string>yes</string>  
    </dict>  
</array>




如果是以dict字典類型開頭的文件,則要換用ValueMap,這是下一篇的內容,先跳過。






2、往ValueVector中插入一個元素。上面有提到過,ValueVector實際上就是一個存放Value類型的vector順序容器,所以它的插入元素方式可以直接使用vector順序容器的操作。示例如下:


1
2
3
4
5
int a = 10;  
std::string b = "star is so cool";  
ValueVector star_val;  
star_val.push_back( Value(a) );  
star_val.push_back( Value(b) );//放入ValueVector前都要先將類型轉成Value類型




3、提取ValueVector中的元素。這裏我接上面的例子來用:


1
2
3
int a1 = star_val.at(0).asInt();  
std::string b1 = star_val.at(1).asString();  
CCLOG("a1 = %d ,b1 = %s",a1,b1);
上面的代碼比較容易理解,就是提取star_val中放在0和1位置上的元素,然後分別轉成int型和string型。asInt()與asString()是Value用來實現類型轉換的函數。






4、刪除ValueVector中的元素,容器中比較常用的刪除元素方式有三種:


1)刪除容器中最後一個元素:


1
star_val.pop_back();//直接刪除容器中最後一個元素




2)用erase刪除容器中的某一個元素?爲什麼我要在前面加個問號呢?假設我要刪除star_val中的 元素a,代碼如下:


1
2
auto star_iter = std::find(star_val.begin(),star_val.end(),a);  
star_val.erase(star_iter);




上面兩行代碼信息量還是比較大的,首先我們要知道erase 刪除的是由一個迭代器指向的單個元素,而不是直接這樣:


1
star_val.erase(a);




這種操作是錯誤的,那麼,什麼是迭代器呢?


假設一個教室就是一個vector容器,每個學生都是vector中的一個元素,而學生對應的座號就是迭代器。






erase裏的參數須是一個迭代器,那麼a元素對應的迭代器是什麼呢?這裏就要用到find算法,它將返回a在容器中的迭代器。






但是,這種刪除元素的方式是無法編譯成功的!因爲Value裏沒有重載==運算符,而std::find裏面的數據類型必須實現==運算符,所以沒法用查找,也就沒法刪除。






3)既然無法刪除指定的元素,那將全部元素都刪除總可以吧?答案是肯定的:


1
star_val.clear();
用clear刪除全部元素。






好了,ValueVector的用法說到這裏,最後做下總結和補充:


1、Vector只能用來存放Ref類型的元素,不能存放數據類型的元素;


2、ValueVector只能用來存放Value類型的元素,因爲Value說到底就是數據類型,所以也可以認爲ValueVector只能用來存放數據類型,千萬別將Ref類型的元素放進入,否則會很刺激。


3、ValueVector中可以放ValueVector,前提是將ValueVector轉成Value類型;而Vector中不能存放Vector類型的元素,如下:


1
2
3
4
5
ValueVector star_val;  
ValueVector star_val2;  
star_val.push_back( Value(star_val2) );//正確  
   
Vector< Vector<Ref*> > star_vec;//錯誤!
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章