取模

对-1取模是现将-1加上除数的整数倍大于零后再取模。

Timer()

变量

		float _elapsed;              // 渡过的时间.
bool _runForever; // 状态变量,标记是否永远的运行。
bool _useDelay; // 状态变量,标记是否使用延迟
unsigned int _timesExecuted; // 记录已经执行了多少次。
unsigned int _repeat; // 定义要执行的总次数,0为1次 1为2次 ……
float _delay; // 延迟多少秒,是指在触发之前的延迟,在触发之后叫间隔,使用interval设置
float _interval; // 时间间隔。 _scheduler(nullptr)
, _elapsed(-1)
, _runForever(false)
, _useDelay(false)
, _timesExecuted(0)
, _repeat(0)
, _delay(0.0f)
, _interval(0.0f)
, _aborted(false)

设置定时器Timer()

void Timer::setupTimerWithInterval(float seconds, unsigned int repeat, float delay)
{
_elapsed = -1; //=-1表示是第一次进入函数,后面的再Update函数中会有初始化操作
_interval = seconds;
_delay = delay;
_useDelay = (_delay > 0.0f) ? true : false;
_repeat = repeat;
_runForever = (_repeat == CC_REPEAT_FOREVER) ? true : false; //CC_REPEAT_FOREVER == -1
_timesExecuted = 0;
}
void Timer::update(float dt)
{
if (_elapsed == -1) //表示第一次进入Update方法,进行初始化
{
_elapsed = 0; //已执行时间
_timesExecuted = 0; //执行次数
return;
} // accumulate elapsed time
_elapsed += dt; //记录累计度过的时间 // deal with delay
if (_useDelay)
{
if (_elapsed < _delay) // 还没有完成延时
{
return;
}
_timesExecuted += 1; // important to increment before call trigger
trigger(_delay); // 【回调函数】
_elapsed = _elapsed - _delay; //此时记录的是开始执行之后度过的时间
_useDelay = false; //延迟结束
// after delay, the rest time should compare with interval
if (isExhausted()) //不永远执行并且已经执行完了重复次数
{ //unschedule timer
cancel();
return;
}
} // if _interval == 0, should trigger once every frame
float interval = (_interval > 0) ? _interval : _elapsed; //正常来说interval都是大于零的,如果interval小于零,那就使用度过的时间来代替间隔时间
//如果度过的时间比间隔要大才会执行
while ((_elapsed >= interval) && !_aborted)
{
_timesExecuted += 1; // important to increment before call trigger
trigger(interval); // 【回调函数】
_elapsed -= interval; //记录剩余时间 if (isExhausted()) //不永远执行并且已经执行完了重复次数
{
cancel();
break;
} if (_elapsed <= 0.f) //间隔时间等于度过的时间
{
break;
}
}
}

一些成员函数

bool TimerTargetSelector::initWithSelector(Scheduler* scheduler, SEL_SCHEDULE selector, Ref* target, float seconds, unsigned int repeat, float delay)
{
_scheduler = scheduler;
_target = target;
_selector = selector; //selector没有set方法,通过此方法来初始化
setupTimerWithInterval(seconds, repeat, delay);
return true;
} void TimerTargetSelector::trigger(float dt) //调用回调函数
{
if (_target && _selector)
{
(_target->*_selector)(dt);
}
} void TimerTargetSelector::cancel()
{
_scheduler->unschedule(_selector, _target);
}
class CC_DLL TimerTargetCallback : public Timer
{
public:
TimerTargetCallback(); // Initializes a timer with a target, a lambda and an interval in seconds, repeat in number of times to repeat, delay in seconds.
bool initWithCallback(Scheduler* scheduler, const ccSchedulerFunc& callback, void *target, const std::string& key, float seconds, unsigned int repeat, float delay); const ccSchedulerFunc& getCallback() const { return _callback; }
const std::string& getKey() const { return _key; } virtual void trigger(float dt) override;
virtual void cancel() override; protected:
void* _target;
ccSchedulerFunc _callback;
std::string _key;
};
//初始化
bool TimerTargetCallback::initWithCallback(Scheduler* scheduler, const ccSchedulerFunc& callback, void *target, const std::string& key, float seconds, unsigned int repeat, float delay)
{
_scheduler = scheduler;
_target = target;
_callback = callback;
_key = key;
setupTimerWithInterval(seconds, repeat, delay);
return true;
}
//调用回调函数
void TimerTargetCallback::trigger(float dt)
{
if (_callback)
{
_callback(dt);
}
}
//
void TimerTargetCallback::cancel()
{
_scheduler->unschedule(_key, _target);

Scheduler()

经常使用的调度器是:

schedulerUpdate()

scheduler(SEL_SCHEDULE selector, float interval, unsigned int repeat, float delay)

scheduleOnce(SEL_SCHEDULE selector, float delay)

变量

pause:启用或暂停此方法。暂停(false),启用(true)

interval:每隔“interval”秒调用一次方法,如果为0,则每一帧都调用,当为0时,建议使用schedulerUpdate。

repeat:触发一次事件后还会触发的次数,为0时触发一次

delay:延迟多少秒,是指在触发之前的延迟,在触发之后叫间隔,使用interval设置

key:用于取消定时器

初始化

typedef struct _listEntry
{
struct _listEntry *prev, *next;
ccSchedulerFunc callback;
void *target;
int priority;
bool paused;
bool markedForDeletion; // selector will no longer be called and entry will be removed at end of the next tick
} tListEntry; //内置的update定时器
typedef struct _hashUpdateEntry
{
tListEntry **list; // Which list does it belong to ?
tListEntry *entry; // entry in the list
void *target;
ccSchedulerFunc callback;
UT_hash_handle hh;
} tHashUpdateEntry; // 自定义定时器
typedef struct _hashSelectorEntry
{
ccArray *timers;
void *target;
int timerIndex;
Timer *currentTimer;
bool currentTimerSalvaged;
bool paused;
UT_hash_handle hh;
} tHashTimerEntry;
float _timeScale;
struct _listEntry *_updatesNegList; // list of priority < 0 三种优先级
struct _listEntry *_updates0List; // list priority == 0
struct _listEntry *_updatesPosList; // list priority > 0
struct _hashUpdateEntry *_hashForUpdates; // hash used to fetch quickly the list entries for pause,delete,etc
std::vector<struct _listEntry *> _updateDeleteVector; // the vector holds list entries that needs to be deleted after update
// Used for "selectors with interval"
struct _hashSelectorEntry *_hashForTimers;
struct _hashSelectorEntry *_currentTarget;
bool _currentTargetSalvaged;
// If true unschedule will not remove anything from a hash. Elements will only be marked for deletion.
bool _updateHashLocked; //初始化
Scheduler::Scheduler(void)
: _timeScale(1.0f) //播放速度为1.0
, _updatesNegList(nullptr) //
, _updates0List(nullptr)
, _updatesPosList(nullptr)
, _hashForUpdates(nullptr)
, _hashForTimers(nullptr)
, _currentTarget(nullptr)
, _currentTargetSalvaged(false)
, _updateHashLocked(false)
#if CC_ENABLE_SCRIPT_BINDING
, _scriptHandlerEntries(20)
#endif
{
// I don't expect to have more than 30 functions to all per frame
//预开辟30个空间,且不希望超过30个函数
_functionsToPerform.reserve(30);
}

哈希表

#include "base/uthash.h"
typedef struct UT_hash_handle {
struct UT_hash_table *tbl;
void *prev; /* prev element in app order */
void *next; /* next element in app order */
struct UT_hash_handle *hh_prev; /* previous hh in bucket order */
struct UT_hash_handle *hh_next; /* next hh in bucket order */
void *key; /* ptr to enclosing struct's key */
unsigned keylen; /* enclosing struct's key len */
unsigned hashv; /* result of hash-fcn(key) */
} UT_hash_handle; HASH_FIND_PTR(head,findptr,out)
HASH_ADD_PTR(head,ptrfield,add)
HASH_DEL(head,delptr) //不同优先级的update定时器的双向链表
typedef struct _listEntry
{
struct _listEntry *prev, *next;
ccSchedulerFunc callback;
void *target;
int priority;
bool paused;
bool markedForDeletion; // selector will no longer be called and entry will be removed at end of the next tick
} tListEntry; //内置的update定时器
typedef struct _hashUpdateEntry
{
tListEntry **list; // Which list does it belong to ?
tListEntry *entry; // entry in the list
void *target;
ccSchedulerFunc callback;
UT_hash_handle hh;
} tHashUpdateEntry; //自定义的定时器
typedef struct _hashSelectorEntry
{
ccArray *timers; //如下
void *target;
int timerIndex;
Timer *currentTimer;
bool paused;
UT_hash_handle hh;
} tHa
typedef struct _ccArray {
ssize_t num, max;
Ref** arr;
} ccArray;
void ccArrayAppendObject(ccArray *arr, Ref* object)
{
CCASSERT(object != nullptr, "Invalid parameter!");
object->retain();
arr->arr[arr->num] = object;
arr->num++;//序号+1
}

构造函数schedule()

//重载版本① 更新选择器 //Ref *target
void Scheduler::schedule(SEL_SCHEDULE selector, Ref *target, float interval, unsigned int repeat, float delay, bool paused)
{
CCASSERT(target, "Argument target must be non-nullptr"); //用来记录一个Ref对象加载的所有timer
tHashTimerEntry *element = nullptr; //_hashForTimers是用来记录tHashTimerEntry头结点的指针
//在_hashForTimers链表中查找与target相等的元素,返回一个element
//#define HASH_FIND_PTR(head,findptr,out)
HASH_FIND_PTR(_hashForTimers, &target, element); //判断有没有找到
if (! element)
{
//为空则先分配空间
element = (tHashTimerEntry *)calloc(sizeof(*element), 1);
//把链表的target指向形参target(把形参的target加入链表)
element->target = target; //再次查找获取链表中的”element“
HASH_ADD_PTR(_hashForTimers, target, element);
+
// Is this the 1st element ? Then set the pause level to all the selectors of this target
//给获取到的element设置paused的状态
element->paused = paused;
}
//target已经加入链表了,判断paused的状态是否设定成功
else
{
CCASSERT(element->paused == paused, "element's paused should be paused.");
} //判断定时器列表的状态,如果为空则分配空间
if (element->timers == nullptr)
{
element->timers = ccArrayNew(10);
}
else
{
//循环定时器结构中的成员
for (int i = 0; i < element->timers->num; ++i)
{
//获取其中的定时器对象
TimerTargetSelector *timer = dynamic_cast<TimerTargetSelector*>(element->timers->arr[i]); //如果定义过定时器,则重新设置interval, repeat, delay
if (timer && !timer->isExhausted() && selector == timer->getSelector())
{
CCLOG("CCScheduler#schedule. Reiniting timer with interval %.4f, repeat %u, delay %.4f", interval, repeat, delay);
timer->setupTimerWithInterval(interval, repeat, delay);
return;
}
}
//检查ccArray的内存,确定可以再添加一个timers
ccArrayEnsureExtraCapacity(element->timers, 1);
} //创建了一个新的TimerTargetSelector对象(timer),用上面处理过的实参对其初始化,并且加到定时器列表(timers)中
TimerTargetSelector *timer = new (std::nothrow) TimerTargetSelector();
timer->initWithSelector(this, selector, target, interval, repeat, delay);
ccArrayAppendObject(element->timers, timer);//object->retain();
timer->release();
}
//重载版本②//使用回调函数callback 多了 形参key和repeat //自定义选择器 void *target
void Scheduler::schedule(const ccSchedulerFunc& callback, void *target, float interval, unsigned int repeat, float delay, bool paused, const std::string& key)
{
CCASSERT(target, "Argument target must be non-nullptr");
CCASSERT(!key.empty(), "key should not be empty!"); tHashTimerEntry *element = nullptr;
HASH_FIND_PTR(_hashForTimers, &target, element); if (! element)
{
element = (tHashTimerEntry *)calloc(sizeof(*element), 1);
element->target = target; HASH_ADD_PTR(_hashForTimers, target, element); // Is this the 1st element ? Then set the pause level to all the selectors of this target
element->paused = paused;
}
else
{
CCASSERT(element->paused == paused, "element's paused should be paused!");
} if (element->timers == nullptr)
{
element->timers = ccArrayNew(10);
}
else
{
for (int i = 0; i < element->timers->num; ++i)
{
TimerTargetCallback *timer = dynamic_cast<TimerTargetCallback*>(element->timers->arr[i]); if (timer && !timer->isExhausted() && key == timer->getKey())
{
CCLOG("CCScheduler#schedule. Reiniting timer with interval %.4f, repeat %u, delay %.4f", interval, repeat, delay);
timer->setupTimerWithInterval(interval, repeat, delay);
return;
}
}
ccArrayEnsureExtraCapacity(element->timers, 1);
} TimerTargetCallback *timer = new (std::nothrow) TimerTargetCallback();
timer->initWithCallback(this, callback, target, key, interval, repeat, delay);
ccArrayAppendObject(element->timers, timer);
timer->release();
}
//重载版本③ //永远执行   repeat = CC_REPEAT_FOREVER
void Scheduler::schedule(const ccSchedulerFunc& callback, void *target, float interval, bool paused, const std::string& key)
{
this->schedule(callback, target, interval, CC_REPEAT_FOREVER, 0.0f, paused, key);
}
//重载版本④ //永远执行 repeat = CC_REPEAT_FOREVER
void Scheduler::schedule(SEL_SCHEDULE selector, Ref *target, float interval, bool paused)
{
this->schedule(selector, target, interval, CC_REPEAT_FOREVER, 0.0f, paused);
}

开启定时器Update()

/*
void Node::scheduleUpdate()
{
scheduleUpdateWithPriority(0);
}
*/
//每帧执行一次,默认优先级为0,优先级越小越先执行
void Node::scheduleUpdateWithPriority(int priority)
{
_scheduler->scheduleUpdate(this, priority, !_running);
}
//默认优先级为0,在所有自定义方法之前执行
template <class T>
void scheduleUpdate(T *target, int priority, bool paused)
{
this->schedulePerFrame([target](float dt){
target->update(dt);
}, target, priority, paused);
}
void Scheduler::schedulePerFrame(const ccSchedulerFunc& callback, void *target, int priority, bool paused)
{
//定义一个hash链表对象
tHashUpdateEntry *hashElement = nullptr;
//查找target,结果通过hashElement返回
HASH_FIND_PTR(_hashForUpdates, &target, hashElement);
//如果找到了
if (hashElement)
{
// change priority: should unschedule it first
//检查优先级是否已经被修改,如果还未修改,先取消修改的计划;
//如果已经被修改,则提示不要重复执行修改操作
if (hashElement->entry->priority != priority)
{
unscheduleUpdate(target);
}
else
{
// don't add it again
CCLOG("warning: don't update it again");
return;
}
} // most of the updates are going to be 0, that's way there
// is an special list for updates with priority 0
//然后将其加入到对应优先级的链表中
if (priority == 0)
{
appendIn(&_updates0List, callback, target, paused);//如下
}
else if (priority < 0)
{
priorityIn(&_updatesNegList, callback, target, priority, paused);//如下
}
else
{
// priority > 0
priorityIn(&_updatesPosList, callback, target, priority, paused);
}
}
//appendIn()用于添加默认优先级的
void Scheduler::appendIn(_listEntry **list, const ccSchedulerFunc& callback, void *target, bool paused)
{
//新建优先级更新链表
tListEntry *listElement = new (std::nothrow) tListEntry(); listElement->callback = callback;
listElement->target = target;
listElement->paused = paused;
listElement->priority = 0;
listElement->markedForDeletion = false; //把listElement加到优先级为0的list中
DL_APPEND(*list, listElement); // update hash entry for quicker access
//创建一个hash链表指针对象
tHashUpdateEntry *hashElement = (tHashUpdateEntry *)calloc(sizeof(*hashElement), 1);
hashElement->target = target;
hashElement->list = list;
hashElement->entry = listElement;
memset(&hashElement->hh, 0, sizeof(hashElement->hh));
//把hashElement添加到_hashForUpdates中
HASH_ADD_PTR(_hashForUpdates, target, hashElement);
}
//priorityIn()用于添加指定优先级
void Scheduler::priorityIn(tListEntry **list, const ccSchedulerFunc& callback, void *target, int priority, bool paused)
{
//同上
tListEntry *listElement = new (std::nothrow) tListEntry(); listElement->callback = callback;
listElement->target = target;
listElement->priority = priority;
listElement->paused = paused;
listElement->next = listElement->prev = nullptr;
listElement->markedForDeletion = false; //判断优先级<0的链表是不是空的
if (! *list)
{
//是空的直接添加
DL_APPEND(*list, listElement);
}
else
{
//设置一个add表示还未添加完成
bool added = false; for (tListEntry *element = *list; element; element = element->next)
{
//如果添加的元素的优先级 < 被循环到的元素的优先级,
//说明插入的元素优先级更高,则会被插入到循环的这个元素之前
if (priority < element->priority)
{
if (element == *list)
{
DL_PREPEND(*list, listElement);
}
else
{
//tListEntry *element = *list的备选路径
listElement->next = element;
listElement->prev = element->prev; element->prev->next = listElement;
element->prev = listElement;
}
//添加完成了
added = true;
break;
}
} // Not added? priority has the higher value. Append it.
//是否添加完成(未完成说明插入的元素在链表中的优先级最低,插入到链表最后)
if (! added)
{
DL_APPEND(*list, listElement);
}
} // update hash entry for quick access
//同上
tHashUpdateEntry *hashElement = (tHashUpdateEntry *)calloc(sizeof(*hashElement), 1);
hashElement->target = target;
hashElement->list = list;
hashElement->entry = listElement;
memset(&hashElement->hh, 0, sizeof(hashElement->hh));
HASH_ADD_PTR(_hashForUpdates, target, hashElement);
}

析构函数~Update()

void Scheduler::unschedule(const std::string &key, void *target)
{
// explicit handle nil arguments when removing an object
if (target == nullptr || key.empty())
{
return;
} //CCASSERT(target);
//CCASSERT(selector); tHashTimerEntry *element = nullptr;
HASH_FIND_PTR(_hashForTimers, &target, element); if (element)
{
for (int i = 0; i < element->timers->num; ++i)
{
TimerTargetCallback *timer = dynamic_cast<TimerTargetCallback*>(element->timers->arr[i]);
//找到要移除的timer
if (timer && key == timer->getKey())
{
if (timer == element->currentTimer && (! timer->isAborted()))
{
timer->retain();//+1
timer->setAborted();//设置延迟释放,下面还要继续访问
}
//在这里调用CC_SAFE_RELEASE -1
ccArrayRemoveObjectAtIndex(element->timers, i, true); // update timerIndex in case we are in tick:, looping over the actions
//个数-1
if (element->timerIndex >= i)
{
element->timerIndex--;
}
//timers里面没有timer了
if (element->timers->num == 0)
{
//标记需要被删除,在刷新函数中执行移除操作
if (_currentTarget == element)
{
_currentTargetSalvaged = true;
}
else
{
//链表中移除 移除之后是安全的了,回到Scheduler::update()中再release()释放
removeHashElement(element);
}
} return;
}
}
}
}

Update()

void Scheduler::update(float dt)
{
//状态锁
_updateHashLocked = true;
//调整时间速率
if (_timeScale != 1.0f)
{
dt *= _timeScale;
} //
// Selector callbacks
// // Iterate over all the Updates' selectors
tListEntry *entry, *tmp; // updates with priority < 0
//先执行优先级高的 priority < 0
DL_FOREACH_SAFE(_updatesNegList, entry, tmp)
{
//对没有暂停的和没有被标记需要删除的执行回调函数
if ((! entry->paused) && (! entry->markedForDeletion))
{
entry->callback(dt);
}
} // updates with priority == 0
DL_FOREACH_SAFE(_updates0List, entry, tmp)
{
if ((! entry->paused) && (! entry->markedForDeletion))
{
entry->callback(dt);
}
} // updates with priority > 0
DL_FOREACH_SAFE(_updatesPosList, entry, tmp)
{
if ((! entry->paused) && (! entry->markedForDeletion))
{
entry->callback(dt);
}
} // Iterate over all the custom selectors
//遍历自定义的定时器
for (tHashTimerEntry *elt = _hashForTimers; elt != nullptr; )
{
_currentTarget = elt; //标记执行到了那个target对象
_currentTargetSalvaged = false; //设置定时器为可用状态 if (! _currentTarget->paused)
{
// The 'timers' array may change while inside this loop
//遍历当前对象的所有定时器
for (elt->timerIndex = 0; elt->timerIndex < elt->timers->num; ++(elt->timerIndex))
{
//标记执行到了那个timer对象
elt->currentTimer = (Timer*)(elt->timers->arr[elt->timerIndex]);
//如果已经被弃用
CCASSERT( !elt->currentTimer->isAborted(),
"An aborted timer should not be updated" );
//
elt->currentTimer->update(dt);//执行定时器的update()!! 在这里执行定时器的回调函数
//如果被弃用了就释放它 执行了上面一条语句之后,已经被从链表中移除了,这时候释放才是安全的
if (elt->currentTimer->isAborted())
{
// The currentTimer told the remove itself. To prevent the timer from
// accidentally deallocating itself before finishing its step, we retained
// it. Now that step is done, it's safe to release it.
elt->currentTimer->release();
}
//当前执行到的timer对象置空
elt->currentTimer = nullptr;
}
} // elt, at this moment, is still valid
// so it is safe to ask this here (issue #490)
//链表继续往后访问
elt = (tHashTimerEntry *)elt->hh.next; // only delete currentTarget if no actions were scheduled during the cycle (issue #481)
//如果被标记弃用并且里面没有timer了,就移除它(定时器已经执行完成了)
if (_currentTargetSalvaged && _currentTarget->timers->num == 0)
{
removeHashElement(_currentTarget);
}
} // delete all updates that are removed in update
//移除所有标记为要删除的update定时器元素
for (auto &e : _updateDeleteVector)
delete e;
//清空待删除数组
_updateDeleteVector.clear(); //移除状态锁
_updateHashLocked = false;
//执行到的target对象置空
_currentTarget = nullptr; #if CC_ENABLE_SCRIPT_BINDING
//
// Script callbacks
// // Iterate over all the script callbacks
if (!_scriptHandlerEntries.empty())
{
for (auto i = _scriptHandlerEntries.size() - 1; i >= 0; i--)
{
SchedulerScriptHandlerEntry* eachEntry = _scriptHandlerEntries.at(i);
if (eachEntry->isMarkedForDeletion())
{
_scriptHandlerEntries.erase(i);
}
else if (!eachEntry->isPaused())
{
eachEntry->getTimer()->update(dt);
}
}
}
#endif
//
// Functions allocated from another thread
// // Testing size is faster than locking / unlocking.
// And almost never there will be functions scheduled to be called.
if( !_functionsToPerform.empty() ) {
_performMutex.lock();
// fixed #4123: Save the callback functions, they must be invoked after '_performMutex.unlock()', otherwise if new functions are added in callback, it will cause thread deadlock.
auto temp = std::move(_functionsToPerform);
_performMutex.unlock(); for (const auto &function : temp) {
function();
}
}
}

removeUpdateFromHash()

void Scheduler::unscheduleUpdate(void *target)
{
if (target == nullptr)
{
return;
} tHashUpdateEntry *element = nullptr;
HASH_FIND_PTR(_hashForUpdates, &target, element);
if (element)
this->removeUpdateFromHash(element->entry); //
}
void Scheduler::removeUpdateFromHash(struct _listEntry *entry)
{
tHashUpdateEntry *element = nullptr; HASH_FIND_PTR(_hashForUpdates, &entry->target, element);
if (element)
{
// list entry
DL_DELETE(*element->list, element->entry);
if (!_updateHashLocked)
CC_SAFE_DELETE(element->entry);
else
{
element->entry->markedForDeletion = true;
_updateDeleteVector.push_back(element->entry); //
} // hash entry
HASH_DEL(_hashForUpdates, element);
free(element);
}
}

一些成员函数

//通过key和target来判断当前对象是否在定时器中
bool isScheduled(const std::string& key, const void *target) const;
//判断条件不同
bool isScheduled(SEL_SCHEDULE selector, const Ref *target) const;
//恢复一个对象的定时器
void resumeTarget(void *target);
//查询一个对象的定时器装状态
bool isTargetPaused(void *target);
//暂停所有的定时器
std::set<void*> pauseAllTargets();
//恢复所有的定时器
void resumeTargets(const std::set<void*>& targetsToResume);

补充

1、为什么使用colloc不使用malloc?
究其根本是malloc和colloc区别的原因,
使用
malloc:(type*)malloc(size) 分配一个size大小的内存空间返回type类型的指针指向内存的首地址
colloc:(type*)colloc(n,size) 分配n个size大小的连续内存空间返回type类型的指针指向第一个内存的首地址
最大的区别是:malloc只分配内存空间不做初始化,原先内存中的数据依然存在,可能会造成数据错误;colloc分配空间后进行初始化,将分配的空间都初始化为0,避免了数据错误。 2、std::move
std::string str = "Hello";
std::vector<std::string> v; v.push_back(str);
std::cout<<v[0]; //输出hello
std::cout<<str; //输出hello v.push_back(std::move(str));
std::cout<<v[0]; //输出hello
std::cout<<v[1]; //输出hello
std::cout<<str; //输出为空

从零开始のcocos2dx生活(三)Scheduler的更多相关文章

  1. 从零开始のcocos2dx生活(十)ScrollView

    目录 简介 基础变量 ScrollViewDelegate Direction _dragging _container _touchMoved _bounceable _touchLength 方法 ...

  2. 从零开始のcocos2dx生活(七)ParticleSystem

    CCParticleSystem是用来设置粒子效果的类 1.粒子分为两种模式:重力模式 和 半径模式 重力模式独占属性: gravity 重力方向,Vec2类型,可以分别指定不同方向的重力大小 spe ...

  3. 从零开始のcocos2dx生活(一)内存管理

    cocos中所有的对象都是继承自Ref基类,Ref的职责就是对对象进行引用计数管理 内存管理中最重要的是三个方法retain().release().autorelease() 在cocos中创建对象 ...

  4. 从零开始のcocos2dx生活(四)ActionManager

    文章目录 初始化构造函数 析构函数 删除哈希元素 分配存放动作对象的空间 通过索引移除动作 暂停动作 恢复动作 暂停所有的动作 恢复所有的动作 添加动作 移除所有的动作 移除target中的所有动作 ...

  5. 从零开始のcocos2dx生活(二)Node

    节点 Node 文章目录 节点 Node 前言 变量初始化 创建一个节点对象 获取节点依赖的计数器 获取节点的描述(获取节点的Tag) 节点的局部层顺序值(LocalZOrder) 设置节点的Loca ...

  6. 从零开始のcocos2dx生活(十一)TableView

    目录 简述 主要变量 主要方法 setVerticalFillOrder reloadData cellAtIndex updateCellAtIndex insertCellAtIndex remo ...

  7. 从零开始のcocos2dx生活(九)CCBReader

    NodeLoaderLibrary是用来存储节点加载器类型的类,通过registerDefaultNodeLoaders()可以注册所有默认类型的加载器 在CocosBuilder的使用手册中: 1. ...

  8. 从零开始のcocos2dx生活(八)ParticleSystemQuad

    https://learnopengl-cn.github.io/01%20Getting%20started/04%20Hello%20Triangle/#_1 写的真的非常好-最近没时间拜读,只看 ...

  9. 从零开始のcocos2dx生活(六)EventDispatcher

    EventDispatcher可能是所有的里面比较不容易理解也不容易看的 我说自己的理解可能会误导到你们-[索了你们看不下去>< 我写了几乎所有的代码的注释,有的是废话跳过就好 主要的代码 ...

随机推荐

  1. iPhone:constrainedToSize获取字符串的宽高

    在使用UILabel存放字符串时,经常需要获取label的长宽数据,本文列出了部分常用的计算方法. 1.获取宽度,获取字符串不折行单行显示时所需要的长度 CGSize titleSize = [aSt ...

  2. js日期拓展方法

    最近项目中使用了大量关于日期的操作遂将其整理如下: /** * 格式化日期 * @param {String} fmt [日期类型 默认为年月日(yyyy-MM-dd)] */ Date.protot ...

  3. 深度学习的Xavier初始化方法

    在tensorflow中,有一个初始化函数:tf.contrib.layers.variance_scaling_initializer.Tensorflow 官网的介绍为: variance_sca ...

  4. 第三期 行为规划——10.用C++实现变道函数

    在之前的测验中,我们设计了一个成本函数,高速公路上到达一个目标选择一条车道. 公式中,Δd是车道间的纵向距离,Δs是车辆到目标之间的距离. 在这个测验中,需要用c++实现代价函数,但是这里有一个变换, ...

  5. HDU1711 Number Sequence 题解 KMP算法

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1711 题目大意:最基础的字符串匹配,只不过这里用整数数组代替了字符串. 给你两个数组 \(a[1..N ...

  6. jq制作tab栏

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  7. Codeforces Round #184 (Div. 2)

    A. Strange Addition (目前的做法好像做烦了) 统计数的\(mask\),表示个.十.百位上是否是0,共8种数. 枚举8种数组成的所有情况\(2^8\),记录最大数量. B. Con ...

  8. Java开发之快捷键

    1.显示桌面快捷键:win+D或者右击状态栏,选择显示桌面. 2.UE编辑器:如果想把多行记录合并为一行,使用替换(Ctrl+R),查找里输入^p(代表回车换行符),替换为里什么都不填,替换位置选择所 ...

  9. Spring Cloud探路(一) Erueka服务器的建立

    组件名:Netflix Eureka  作用:支撑微服务的自注册.自发现,提供负载均衡能力 开发环境使用IDEA 1.新建Eureka Server,新建maven项目,配置pom.xml <p ...

  10. H3C 配置路由器作为FTP客户端