1

(对不起我的英语不好!)你好,我正在学习工业计算。这是我的第五个任期。在我的一门课程中,老师要求我们在 c 中编写一个类“列表”(仅转发,指向下一个元素但不是前一个元素)。我已经完成了一个简单的“提取”方法,它返回一个 value_type 并删除第一个元素,但我的老师说,如果复制构造函数抛出异常,该值将丢失,因为它已被提取。我查看了 std::forward_list 并且我看到 stl 提供了一种提取方法和一种访问最后一个元素的方法( pop_front() 和 front() )。我不明白为什么我们不能用一种方法来做到这一点?我写了这段代码:

value_type extract()
{
   if( empty() )
     throw EmptyList();
   std::unique_ptr< Node > p = m_Head;
   if( m_Head == m_Last )
     m_Last = nullptr;
   m_Head = m_Head->m_Next;
   try
   {
       return p->m_Value;
   }
   catch( ... )
   {
       m_Head = p.release();
       if( !m_Last )
          m_Last = m_Head;
       throw;
   }
}

我还制作了一个带有 nothrow_t 参数的方法提取,该参数不复制任何内容( return void ),因此我们可以“强制”提取。为什么 stl 不这样做?做“auto v = something.pop();”会很好 !我的版本安全吗?如果我们真的关心可以在复制构造函数中抛出的异常并且我们真的想继续并保留数据,我们不能简单地尝试围绕“pop”方法进行捕获吗?

谢谢你的帮助!

4

2 回答 2

0

STL 使它有两个操作,因为 front() 函数返回一个引用,而不是一个值,以消除双重副本。在返回项目时从列表中删除项目将需要按值返回,否则该对象将已经超出范围。

于 2013-09-24T00:51:04.407 回答
0

我不明白为什么我们不能用一种方法来做到这一点?

需要这两个操作来给出一个强有力的异常保证——即如果任何时候抛出异常,容器不应该被改变。

您的提案auto v = something.pop()必须按顺序执行三件事:

  • 从容器中移除一个元素;
  • 从函数中返回该元素;
  • v用返回值初始化。

如果最后阶段抛出异常,则该元素将已从容器中删除并丢失。您尝试使用try...catch构造来解决此问题将无济于事:从函数返回后将引发异常,因此不会被函数中的处理程序捕获。

使用 STL 风格的界面分两个阶段执行此操作,顺序为:

  • 处理对容器元素的引用;
  • 从容器中移除元素。

现在,如果处理抛出异常,则元素保留在容器中并且不会丢失。

如今,通过将移动语义添加到语言中,您可以实现您的版本,并要求元素在移动时不会抛出。这是一个相当合理的要求,已经在 C++11 容器上进行了一些操作。但这并没有发生,我怀疑这样的改变只是为了一点点方便。

如果我们真的关心可以在复制构造函数中抛出的异常并且我们真的想继续并保留数据,我们不能简单地尝试围绕“pop”方法进行捕获吗?

我们可以; 但这比提供异常安全的接口更不方便,而且更容易出错。

于 2013-09-24T01:56:51.283 回答