2010-07-15 3 views
0

У меня есть контейнер STL, и мне нужно выполнить действие для каждого элемента в контейнере. Но если действие не выполняется для любого элемента, я хочу изменить действие на любые элементы, которые уже были изменены.Идите вперед, а затем переверните контейнер STL

Например, если у меня был вектор STL с указателями на числовые классы bankAccount и вы хотели увеличить их на 50 долларов. Но если какой-либо из банковских счетов не увеличится на 50, я хочу полностью отменить увеличение и уменьшить на 50 долларов любую из уже увеличенных учетных записей.

std::vector<bankAccount*> bankAccounts; 
std::vector<bankAccount*>::iterator iter; 

for (iter = bankAccounts.begin(); iter != bankAccounts.end(); ++iter) 
{ 
    try 
    { 
     iter->increaseBalance(50); 
    } 
    catch (...) 
    { 
     // One of the bankAccounts failed to increase by 50, now I need to go 
     // back and decrease by 50 all of the bankAccounts that have already 
     // been increased. 
    } 
} 

Есть ли элегантный способ сделать это? Может быть, с алгоритмами STL или с использованием обратных итераторов?

+1

напишите * functor * и используйте * for_each * –

+1

Возможно ли выполнить цикл и просто проверить, будет ли операция успешной? Затем вы просто убедитесь, что все будут успешными, и если да, выполните действие. – GManNickG

+0

Вы должны поймать конкретное исключение, которое, как вы ожидаете, может быть выброшено. Если у вас есть блок 'catch (...)', вы не можете знать, какое исключение было выбрано, и единственные разумные способы оставить такой блокирующий блок - либо перестроить исключение, либо прекратить действие приложения. –

ответ

9

Вот что я хотел бы сделать:

  • Переместить попробовать/поймать вне цикла
  • Создать дубликат bankAccounts контейнера
  • перебрать дубликат контейнера, вызывая increaseBalance по каждому пункту
  • Если петля успешно завершена, swap() оригинал и дублирующий контейнер

Код будет выглядеть примерно так:

std::vector<bankAccount> bankAccounts; 
... 
std::vector<bankAccount> tmp(bankAccounts); 

try 
{ 
    for (iter = tmp.begin(); iter != tmp.end(); ++iter) 
    { 
    iter->increaseBalance(50); 
    } 
    bankAccounts.swap(tmp); 
} 
catch (...) 
{ 
} 

Обратите внимание, что проведение указателя на объект внутри std::vector, как правило, не так хорошо, идеи, как контейнер ожидает, что данные, хранящиеся в нем, чтобы иметь значение семантики, не указатель семантики. Это может привести к оборванным указателям, утечкам памяти и также требует дополнительного кода очистки, который вам не нужен (для удаления элементов в контейнере вручную). С помощью приведенного выше кода я переключился на хранение данных внутри вектора, если это не вариант, который вам нужен, чтобы убедиться, что вы используете глубокую копию вручную при копировании вектора.

На самом деле, вы можете уменьшить код на следующее, если вы предполагаете, одни и те же определения для bankAccounts и tmp:

std::for_each(tmp.begin(), tmp.end(), 
       std::mem_fun_ref(&bankAccount::increaseBalance, 50)); 
bankAccounts.swap(tmp); 

Основное преимущество приведенного выше кода является то, что в обоих случаях, это исключение безопасно без любая дополнительная специальная обработка.

+0

Замените 'for' на' for_each' и вызов указателя с 'mem_fun' – wheaties

+0

Вам даже не нужен try/catch, если вы действительно не хотите делать что-то еще. Если boostBalance не сработает, своп просто не произойдет. И да, я бы переключился на for_each. +1 хотя. –

+0

Да, 'try/catch' на самом деле не нужен в этом контексте. Я бы, конечно, оставил его полностью, но я также попытался перестроить код OP, сохранив большую часть его. –

1

Я думаю, что более элегантный подход будет рассматривать операцию как транзакцию. Другими словами, создайте заменяемую копию учетных записей и замените оригинал на успех.

Смежные вопросы