(18)数据结构
2016-02-03 14:13
543 查看
cocos2d::Vector
v3.0 beta加入定义在"COCOS2DX_ROOT/cocos/base"的"CCVector.h"头文件中。
template<class T>class CC_DLL Vector;
cocos2d::Vector<T>是一个封装好的能动态增长顺序访问的容器。
cocos2d::Vector<T>中的元素是按序存取的,它的低层实现数据结构是标准模版库中的标准顺序容器
std::vector。 在Cocos2d-x v3.0 beta之前,使用的是另外一个顺序访问容器
cocos2d::CCArray,不过它将会被废弃。 设计者们将
cocos2d::Vector<T>设计为
cocos2d::CCArray的替代品,所以建议优先考虑使用
cocos2d::Vector<T>。
cocos2d::Vector<T>的一些操作的时间复杂度如下:
随机访问,O(1)
将元素插入到尾部或者删除尾部的元素,O(1)
随机插入或删除, O(n)
模版参数
T - 元素类型T的类型必须是继承自
cocos2d::Object类型的指针。因为已经将Cocos2d-x的内存管理模型集成到了
cocos2d::Vector<T>中,所以类型参数不能是其他的类型包括基本类型。
内存管理
cocos2d::Vector<T>类只包含一个成员数据:
std::vector<T> _data;
_data的内存管理是由编译器自动处理的,如果声明了一个
cocos2d::Vector<T>类型,就不必费心去释放内存。 注意:使用现代的c++,本地存储对象比堆存储对象好。所以请不要用new操作来申请
cocos2d::Vector<T>的堆对象,请使用栈对象。 如果真心想动态分配堆
cocos2d::Vector<T>,请将原始指针用智能指针来覆盖。 警告:
cocos2d::Vector<T>并不是
cocos2d::Object的子类,所以不要像使用其他cocos2d类一样来用retain/release和引用计数内存管理。
基本用法
作者们用std::vector<T>的基本操作加上Cocos2d-x的内存管理规则来覆盖该模版原先的普通操作。 所以pushBack()操作将会保留传递过来的参数,而popBack()则会释放掉容器中最后的一个元素。 当你使用这些操作的时候,你需要特别注意这些受托管的对象,对于新手来说,这往往是陷阱。 警告:
cocos2d::Vector<T>并没有重载[]操作,所以不能直接用下标[i]来获取第i位元素。
cocos2d::Vector<T>提供了不同类型的迭代器,所以我们可以受益于c++的标准函数库,我们可以使用大量标准泛型算法和for_each循环。 除了std::vector容器的操作之外,开发者们还加入许多标准算法诸如:
std::find,
std::reverse和
std::swap,这些算法可以简化很多通用的操作。 要了解更多的api用例,可以参考Cocos2d-x 3.0beta的源码和压缩包里附带的例子。 下面是一些简单的例子:
//create Vector<Sprite*> with default size and add a sprite into it auto sp0 = Sprite::create(); sp0->setTag(0); //here we use shared_ptr just as a demo. in your code, please use stack object instead std::shared_ptr<Vector<Sprite*>> vec0 = std::make_shared<Vector<Sprite*>>(); //default constructor vec0->pushBack(sp0); //create a Vector<Object*> with a capacity of 5 and add a sprite into it auto sp1 = Sprite::create(); sp1->setTag(1); //initialize a vector with a capacity Vector<Sprite*> vec1(5); //insert a certain object at a certain index vec1.insert(0, sp1); //we can also add a whole vector vec1.pushBack(*vec0); for(auto sp : vec1) { log("sprite tag = %d", sp->getTag()); } Vector<Sprite*> vec2(*vec0); if (vec0->equals(vec2)) { //returns true if the two vectors are equal log("pVec0 is equal to pVec2"); } if (!vec1.empty()) { //whether the Vector is empty //get the capacity and size of the Vector, noted that the capacity is not necessarily equal to the vector size. if (vec1.capacity() == vec1.size()) { log("pVec1->capacity()==pVec1->size()"); }else{ vec1.shrinkToFit(); //shrinks the vector so the memory footprint corresponds with the number of items log("pVec1->capacity()==%zd; pVec1->size()==%zd",vec1.capacity(),vec1.size()); } //pVec1->swap(0, 1); //swap two elements in Vector by their index vec1.swap(vec1.front(), vec1.back()); //swap two elements in Vector by their value if (vec2.contains(sp0)) { //returns a Boolean value that indicates whether object is present in vector log("The index of sp0 in pVec2 is %zd",vec2.getIndex(sp0)); } //remove the element from the Vector vec1.erase(vec1.find(sp0)); //pVec1->erase(1); //pVec1->eraseObject(sp0,true); //pVec1->popBack(); vec1.clear(); //remove all elements log("The size of pVec1 is %zd",vec1.size()); }
输出:
Cocos2d: sprite tag = 1 Cocos2d: sprite tag = 0 Cocos2d: pVec0 is equal to pVec2 Cocos2d: pVec1->capacity()==2; pVec1->size()==2 Cocos2d: The index of sp0 in pVec2 is 0 Cocos2d: The size of pVec1 is 0
最佳做法
考虑基于栈的cocos2d::Vector<T>优先用于基于堆的
当将
cocos2d::Vector<T>作为参数传递时,将它声明成常量引用:
const cocos2d::Vector<T>&
返回值是
cocos2d::Vector<T>时,直接返回值,这种情况下编译器会优化成移动操作。
不要用任何没有继承cocos2d::Object的类型作为
cocos2d::Vector<T>的数据类型。
cocos2d::Map<k,v>
v3.0 beta加入定义在"COCOS2DX_ROOT/cocos/base"的"CCMap.h"头文件中。
template <class K, class V> class CC_DLL Map;
cocos2d::Map<K,V>是使用
std::unordered_map作为底层结构的关联式容器。 而
std::unordered_map是一个存储键值对的关联式容器,它可以通过它们的键快速检索对应的值。 使用unordered_map,键通常是唯一的,而值则与这个键对应。
在unordered_map内部,元素是无序,它们是根据键的哈希值来存取的,存取的时间复杂度是常量,超级快。
在Cocos2d-x v3.0beta之前,使用的是另外一种顺序式容器
cocos2d::CCDictionary,不过它将很快被废弃。
设计者们谨慎地设计了
cocos2d::Map<K,V>用来替代
cocos2d::CCDictionary,所以应该尽量使用
cocos2d::Map而不是
cocos2d::CCDictionary
模版参数
cocos2d::Map<K,V>类只包含一个数据成员:
typedef std::unordered_map<K, V> RefMap; RefMap _data;
_data的内存管理是由编译器处理的,当在栈中声明
cocos2d::Map<K,V>对象时,无需费心释放它占用的内存。 但是如果你是使用
new操作来动态分配
cocos2d::Map<K,V>的内存的话,就得用
delete来释放内存了,
new[]操作也一样。
注意:使用现代的c++,本地存储对象比堆存储对象好。所以请不要用
new操作来分配
cocos2d::Map<K,V>的堆对象,请使用栈对象。
如果真心想动态分配堆
cocos2d::Map<K,V>,请将原始指针用智能指针来覆盖。
警告:
cocos2d::Map<K,V>并不是
cocos2d::Object的子类,所以不要像使用其他cocos2d类一样来用retain/release和引用计数内存管理。
基本用例
警告:cocos2d::Map<K,V>并没有重载[]操作,不要用下标[i]来取
cocos2d::Map<K,V>对象中的元素。
为了解更多的用例,你得参考源码和压缩文件中附带的例子。 下面是一些简单操作的用例:
//create Map<K, V> with default size and add a sprite into it auto sp0 = Sprite::create(); sp0->setTag(0); Map<std::string, Sprite*> map0; std::string mapKey0 = "MAP_KEY_0"; map0.insert(mapKey0, sp0); log("The size of map is %zd.",map0.size()); //create a Map<K, V> with capacity equals 5 Map<std::string, Sprite*> map1(map0); std::string mapKey1 = "MAP_KEY_1"; if(!map1.empty()){ auto spTemp = (Sprite*)map1.at(mapKey0); log("sprite tag = %d", spTemp->getTag()); auto sp1 = Sprite::create(); sp1->setTag(1); map1.insert(mapKey1, sp1); //get all keys,stored in std::vector, that matches the object std::vector<std::string> mapKeyVec; mapKeyVec = map1.keys(); for(auto key : mapKeyVec) { auto spTag = map1.at(key)->getTag(); log("The Sprite tag = %d, MAP key = %s",spTag,key.c_str()); log("Element with key %s is located in bucket %zd",key.c_str(),map1.bucket(key)); } log("%zd buckets in the Map container",map1.bucketCount()); log("%zd element in bucket 1",map1.bucketSize(1)); //get a random object if the map isn't empty, otherwise it returns nullptr log("The random object tag = %d",map1.getRandomObject()->getTag()); //find(const K& key) can be used to search the container for an element with 'key' //erase(const_iterator position) remove an element with an iterator log("Before remove sp0, size of map is %zd.",map1.size()); map1.erase(map1.find(mapKey0)); log("After remove sp0, size of map is %zd.",map1.size()); } //create a Map<K, V> with capacity equals 5 Map<std::string, Sprite*> map2(5); map2.reserve(10); //set capacity of the map
结果是:
cocos2d: The size of map is 1. cocos2d: sprite tag = 0 cocos2d: The Sprite tag = 1, MAP key = MAP_KEY_1 cocos2d: Element with key MAP_KEY_1 is located in bucket 1 cocos2d: The Sprite tag = 0, MAP key = MAP_KEY_0 cocos2d: Element with key MAP_KEY_0 is located in bucket 0 cocos2d: 2 buckets in the Map container cocos2d: 1 element in bucket 1 cocos2d: The random object tag = 0 cocos2d: Before remove sp0, size of map is 2. cocos2d: After remove sp0, size of map is 1.
最佳用法
将
cocos2d::Map<K,V>()作为参数传递时,将它声明为常量引用
const cocos2d::Map<K,V>()&
V类型必须为
cocos2d::Object的子类指针,不能用其他的类型包括基本类型。
cocos2d::Value
于v3.0beta加入定义在"COCOS2DX_ROOT/cocos/base"的头文件"CCValue.h"中
class Value;
cocos2d::Value是许多基本类型(
int,
float,
double,
bool,
unsigned char,char*和
std::string)还有
std::vector<Value>,
std::unordered_map<std::string,Value>和
std::unordered_map<int,Value>这些类的包装类型。
你可以将上面提及的基本类放进
cocos2d::Value对象将它们转换成对应的类型,反之亦然。
cocos2d::Value底层用一个统一的变量来保存任意基本类型值,它将更加节省内存。在
Cocos2d-x v3.0beta之前使用的是
CCBool,
CCFloat,
CCDouble和
CCInteger这样基本类型包装类,不过它们将被废弃掉。
注意:当处理基本类型和和容器时,请使用
cocos2d::Vector<T>,
cocos2d::Map<K,V>和
cocos2d::Value。
内存管理
cocos2d::Value的内存由它的析构函数来释放,所以使用
cocos2d::Value时请尽量用推荐的最佳做法。
cocos2d::Value包含下面的数据成员:
union { unsigned char byteVal; int intVal; float floatVal; double doubleVal; bool boolVal; }_baseData; std::string _strData; ValueVector* _vectorData; ValueMap* _mapData; ValueMapIntKey* _intKeyMapData; Type _type;
代码段中,
_baseData,
_strData和
_type是由编译器和它们的析构函数负责释放内存的,而
cocos2d::Value的析构函数则负责释放指针成员(
_vectorData,
_mapData和
intKeyMapData)。
注意:
cocos2d::Value不能像其它cocos2d类型一样使用retain/release和refcount内存管理
基本用法
ocos2d::Value的用法非常简单直接。 下面就是使用的例子:
Value val; // call the default constructor if (val.isNull()) { log("val is null"); }else{ std::string str =val.getDescription(); log("The description of val0:%s",str.c_str()); } //---------------------------------------------------- Value val1(65); // initialize with a integer //Value val1(3.4f); // initialize with a float value //Value val1(3.5); // initialize with a double value log("The description of the integer value:%s",val1.getDescription().c_str()); log("val1.asByte() = %c",val1.asByte()); //---------------------------------------------------- std::string strV = "string"; Value val2(strV); // initialize with string log("The description of the string value:%s",val2.getDescription().c_str()); //---------------------------------------------------- auto sp0 = Sprite::create(); Vector<Object*>* vecV = new Vector<Object*>(); vecV->pushBack(sp0); Value val3(vecV); // initialize with Vector log("The description of the Vector value:%s",val3.getDescription().c_str()); delete vecV; //---------------------------------------------------- Map<std::string, Object*>* mapV = new Map<std::string, Object*>(); mapV->insert(strV,sp0); Value val4(mapV); // initialize with Map log("The description of the Map value:%s",val4.getDescription().c_str()); delete mapV; //---------------------------------------------------- Value val6(&val4); // initialize with Map log("The description of the Value-type value:%s",val6.getDescription().c_str()); //---------------------------------------------------- val2 = val1; // assigning between 2 Value-type log("operator-> The description of val2:%s",val2.getDescription().c_str()); val2 = 4; //assigning directly log("operator-> The description of val4:%s",val2.getDescription().c_str());
结果是:
cocos2d: val is null cocos2d: The description of the integer value: 65 cocos2d: val1.asByte() = A cocos2d: The description of the string value: string cocos2d: The description of the Vector value: true cocos2d: The description of the Map value: true cocos2d: The description of the Value-type value: true cocos2d: operator-> The description of val2: 65 cocos2d: operator-> The description of val4: 4
最佳用法
考虑使用cocos2d::Value和新的模版容器(cocos2d::Vector和cocos2d::Map<k,v>)优于使用cocos2d::CCBool, cocos2d::CCFloat,cocos2d::CCDouble,cocos2d::CCString,cocos2d::CCInteger和旧的objective-c风格的容器(cocos2d::CCArray和cocos2d::CCDictionary)。当要使用基本类型的聚合时,将基本类型包装成cocos2d::Value,然后将它们和模版容器cocos2d::Vector和cocos2d::Map<k,v>联合使用。
相关文章推荐
- 【数据结构学习笔记】——二叉树的建立、交换、求宽度
- 数据结构——排序算法总结
- Python内建数据结构详解
- 数据结构基础之后缀表达式与中缀表达式的相互推导
- 数据结构图文解析之:队列详解与C++模板实现
- Python内建数据结构详解
- MySQL索引背后的数据结构及算法原理
- 数据结构学习地址--上海交大acm试点班总教头俞勇老师讲的哦,还有其他课程地址,超全icourse
- 数据结构基础之栈与递归
- 数据结构基础之链表
- Python Redis数据结构服务器
- 哈夫曼树与哈夫曼编码
- 【数据结构】邻接表表示法的图的深度广度优先遍历递归和非递归遍历
- 【数据结构】邻接矩阵表示法的图的深度广度优先遍历递归和非递归遍历
- nginx学习六 高级数据结构之双向链表ngx_queue_t
- 数据结构之链表队列基本操作
- [数据结构与算法分析] 链表的游标实现
- [数据结构与算法分析] 单链表基本操作的实现
- [数据结构与算法分析] 求连续子数组的最大和问题
- 数据结构:JavaScript实现散列