4

我正在讨论如何正确处理容器作为参数。

我们有一个接受容器参数的函数,并希望返回仅填充了函数放入其中的容器:

class bar;

void foo(std::vector<bar> &bars) 
{
   //do stuff that fills bars.
   //exceptions may be thrown.
   //we may also legally return early 
   return;
}

在讨论的一方面,有人说我们应该bars.clear()先运行该函数。

例如:

void foo(std::vector<bar> &bars) 
{
   bars.clear();
   //do stuff that fills bars.
   //exceptions may be thrown.
   //we may also legally return early 
   return;
}

我自己的偏好是尽可能接近强异常保证,这意味着制作一个本地容器,填充它并在返回之前进行交换,否则保持bars不变。

例如:

void foo(std::vector<bar> &bars) 
{
   std::vector<bar> localBars;
   //do stuff that fills localBars.
   //exceptions may be thrown.
   //we may also legally return early 
   if (returnEarly)
   {
       bars.swap(localBars);
       return;
   }
   //do more stuff that may change localBars

   bars.swap(localBars);
   return;
}

第一个例子是“经典”方法;在做任何事情之前清除你的参数并从那里开始。

对我来说,第二种方法设置了强大的异常保证(假设函数所做的任何事情都不能改变内部状态),并且避免了 clear() 调用。

选择一种方法而不是另一种方法有什么优点或缺点吗?

请注意,对于此功能,不需要强大的异常保证;如果函数在参数中没有失败,或者在它到达异常处理程序时,它所做的任何事情都会很重要。

4

3 回答 3

1

您提到了使用交换的一个优点:它提供了强大的异常保证。这是否真的重要是另一个问题:如果客户端传递的参数会在异常传播时立即被破坏(在我看到的代码中几乎总是这种情况),那真的没关系。

还有另一个重要区别:clear()不释放内存或减少容量。如果客户端代码在循环中调用您的函数,并且在循环外部定义了向量,则向量将很快达到其最大大小,并且在填充它时不会有任何重新分配。当然,使用交换策略,您总是会重新分配。如果你不关心这种性能问题,你应该返回向量,而不是将非常量引用作为参数。

于 2012-04-12T17:07:52.493 回答
1

swap() 在异常方面更好。如果容器很大并且您有内存限制,则 clear 可能具有优势。更实用/更清洁的风格可以是:

std::vector<bar> void foo()
{
    std::vector<bar> bars;
    ...
    return bars;
}

RVO 将在大部分时间处理它,并且使用 c++11 并移动构造函数/赋值它将非常有效。

于 2012-04-12T17:08:33.060 回答
1

两者真的很不一样。

优点clear

  • 重用内存
  • 没有留下过时的信息bars

优点swap

  • 强大的异常保证

这个问题一般不能回答,你在你的情况下寻求什么语义/保证?

于 2012-04-12T17:38:40.787 回答