Здравствуйте, Anton_86, Вы писали:
<>
Вообще-то, если коллекция пуста, то непонятно, что возвращать.
Т.е.
TRet Invoke(PARAMS)
{
if(!m_DelegateList.empty())
{
for(......) (*it)->Invoke(ARGS);
return m_DelegateList.back()->Invoke(ARGS);
}
else
{
// на выбор
assert(! "нечего вызывать, нечего возвращать, щас вылечу нафиг");
throw SomeDelegateException();
return TRet();
}
}
А ещё остаётся за кадром такой момент, как изменение списка во время его обхода.
Тут нужна или двойная буферизация,
vector<IDelegate*> snapshot; // вектор дешевле списка, поэтому копировать удобнее в него
snapshot.reserve(m_DelegateList.size());
snapshot.insert(m_DelegateList.begin(), m_DelegateList.end());
// далее - как с гусём :)
... или внимательность к свежедобавленным и свежеудаляемым элементам.
Свежедобавленные в конец — будут учитываться, поскольку мы проверяем фактический конец на каждой итерации цикла. (А вот если бы использовали for_each, то там запомнили бы итератор на предпоследний элемент на момент начала работы).
Свежеудалённые — болезненнее, так как может быть удалён текущий элемент, а итератор на него станет невалидным.
Не реентерабельная версия Invoke — в паре с Remove и RemoveAll может выглядеть так
DelegateList::iterator m_next;
.....
void Remove(IComparableDelegate* pDelegate)
{
.....
if(it == m_next)
++m_next;
m_DelegateList.erase(it);
.....
}
void RemoveAll()
{
.....
m_next = m_DelegateList.end();
}
TRet Invoke(PARAMS)
{
DelegateList::iterator it = m_DelegateList.begin();
while(true)
{
if(it == m_DelegateList.end()) { assert(!"всё-таки вылетели!!!"); return TRet(); }
m_next = it; ++m_next;
if(m_next == m_DelegateList.end()) { return (*it)->Invoke(ARGS); }
(*it)->Invoke(ARGS);
it = m_next;
}
}
Реентерабельная... у, это сложно.