【问题标题】:Iterate forward and then reverse over STL container向前迭代然后反向遍历 STL 容器
【发布时间】:2010-07-15 18:49:07
【问题描述】:

我有一个 STL 容器,我需要对容器中的每个元素执行一个操作。但是,如果对任何元素的操作失败,我想对已更改的任何元素撤消操作。

例如,如果我有一个带有指向数字 bankAccount 类的指针的 STL 向量,并且想要将每个类增加 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 算法或使用反向迭代器?

【问题讨论】:

  • 写一个 functor 并使用 for_each
  • 是否可以循环遍历,只检查操作是否成功?然后,您只需确保它们都会成功,如果成功,请执行操作。
  • 您应该捕获您期望可能引发的特定异常。如果您有一个catch (...) 块,您将无法知道引发了什么异常,并且离开此类 catch 块的唯一合理方法是重新引发异常或终止应用程序。

标签: c++ stl


【解决方案1】:

我会这样做:

  • 将 try/catch 移出循环
  • 创建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 中保存指向对象的指针通常不是一个好主意,因为容器希望存储在其中的数据具有值语义,而不是指针语义。这可能会导致悬空指针、内存泄漏,并且还需要额外的清理代码,否则您不需要(手动删除容器中的项目)。使用上面的代码,我已经切换到将数据保存在向量中,如果这不是一个选项,您需要确保在复制向量时使用手动深拷贝。

实际上,如果您假设bankAccountstmp 的定义相同,则可以将代码简化为以下内容:

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

上述代码的主要优点是,在这两种情况下,它都是异常安全的,无需任何进一步的特殊处理。

【讨论】:

  • for_each替换for,用mem_fun替换指针调用
  • 除非你真的想做其他事情,否则你甚至不需要 try/catch。如果 increaseBalance 在任何地方失败,交换就不会发生。是的,我也会切换到 for_each 。虽然 +1。
  • 是的,try/catch 在这种情况下并不是必需的。我通常会完全忽略它,但我也尝试在保留大部分代码的同时重构 OP 的代码。
  • std::vector&lt;Account&gt; tmp; std::transform( acc.begin(), acc.end(), std::back_inserter(tmp), boost::bind( &amp;Account::increaseBalance, _1, 50) ); std::swap( tmp, acc );
  • 感谢您的解决方案。看起来根本问题是我需要在保存结果之前尝试对所有元素执行操作。为什么解决方案异常末尾的代码是安全的?
【解决方案2】:

我认为更优雅的方法是将操作视为事务。换句话说,创建帐户的替换副本,并在成功时覆盖原始副本。

【讨论】:

    猜你喜欢
    • 2015-07-13
    • 2020-11-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-01-22
    • 2023-03-17
    • 2011-05-11
    相关资源
    最近更新 更多