Выполните итерацию вперед и затем назад через контейнер STL - PullRequest
0 голосов
/ 15 июля 2010

У меня есть контейнер 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 или с использованием обратных итераторов?

Ответы [ 2 ]

9 голосов
/ 15 июля 2010

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

  • Переместить попытку / перехватить за пределы цикла
  • Создать дубликат контейнера 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);

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

1 голос
/ 15 июля 2010

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...