取模

对-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. 阿里云亮相2019联通合作伙伴大会,边缘计算等3款云产品助力5G时代产业数字化转型

    4月23日,2019中国联通合作伙伴大会在上海正式开幕,本次大会以“合作不设限,共筑新生态”为主题,涉及5G.边缘计算.云计算.物联网.新媒体.人工智能.互联网化等各领域超过600家合作伙伴与3万名各 ...

  2. async/await运用-前端表单弹窗验证同步书写方式(React)

    在前端项目中,我们经常会碰到这样的场景: 当前我们有一个表单需要填写,在完成表单填写后经过校验之后会弹出短信或者其他形式验证码,进行补充校验,然后一起提交给接口. 场景如下图: 当前为创建操作,编辑操 ...

  3. oracle函数 last_day(d1)

    [功能]:返回日期d1所在月份最后一天的日期. [参数]:d1,日期型 [返回]:日期 [示例]select sysdate,last_day(sysdate)  hz from dual; 返回:2 ...

  4. H3C 命令行帮助特性(续)

  5. 传说中Python最难理解的点|看这完篇就够了(装饰器)

    https://mp.weixin.qq.com/s/B6pEZLrayqzJfMtLqiAfpQ 1.什么是装饰器 网上有人是这么评价装饰器的,我觉得写的很有趣,比喻的很形象 每个人都有的内裤主要是 ...

  6. 2018-8-10-git-使用-VisualStudio-比较分支更改

    title author date CreateTime categories git 使用 VisualStudio 比较分支更改 lindexi 2018-08-10 19:16:52 +0800 ...

  7. H3C 10BASE-T线缆和接口

  8. 快速理解bootstrap,bagging,boosting,gradient boost-三个概念

      1 booststraping:意思是依靠你自己的资源,称为自助法,它是一种有放回的抽样方法,它是非参数统计中一种重要的估计统计量方差进而进行区间估计的统计方法. 其核心思想和基本步骤如下: (1 ...

  9. HDU 5463

    题意:一个盒子有36个格子.每个格子可以装64个物品,搬运一个箱子是一次搬运,问最少到搬运次数 思路:直接求总需要多少个格子,然后去求盒子,这里求盒子呢有个小技巧,就是用ceil函数 #include ...

  10. java TreeSet的排序之定制排序

    TreeSet的自然排序是根据元素的大小进行升序排序的,若想自己定制排序,比如降序排序,就可以使用Comparator接口了: 该接口包含int compare(Object o1,Object o2 ...