谁应该清除通过引用检索的向量?

Who should clear a vector retrieved by reference?

假设我有这个非常简单的代码:

  std::vector<int> getVector( int size )
  {
      std::vector<int> res;
      for ( size_t i = 0; i != size; ++i )
          res.push_back( i* 23 );
      return res;
  }
  
  int main ()
  {
      std::vector<int> v;
      v = getVector(10);
      std::cout << "Size1 is " << v.size() << std::endl;
      v = getVector(15);
      std::cout << "Size2 is " << v.size() << std::endl;
  }

它输出:

Size1 is 10

Size2 is 15

现在,假设我想更改 getVector 以避免无用的对象副本并优化速度和内存使用。所以 getVector 现在将获得对要填充的向量的引用。

  void getVector( int size, std::vector<int>& res )
  {
      for ( size_t i = 0; i != size; ++i )
          res.push_back( i* 23 );
  }

现在,如果我在不注意的情况下更改 main 函数:

  int main ()
  {
      std::vector<int> v;
      getVector(10,v);
      std::cout << "Size1 is " << v.size() << std::endl;
      getVector(15,v);
      std::cout << "Size2 is " << v.size() << std::endl;
  }

它输出:

Size1 is 10

Size2 is 25

这不是我最初拥有的。

我做过很多次这样的改变(用引用传递的对象替换返回值),我一直在问自己同样的问题:谁应该清除向量?

是否有任何“指南”或“一般规则”说明在这种情况下谁应该清除向量?

函数本身应该做吗?

  void getVector( int size, std::vector<int>& res )
  {
      res.clear();
      for ( size_t i = 0; i != size; ++i )
          res.push_back( i* 23 );
  }

还是应该由来电者来做?

  int main ()
  {
      std::vector<int> v;
      getVector(10,v);
      std::cout << "Size1 is " << v.size() << std::endl;
      v.clear();
      getVector(15,v);
      std::cout << "Size2 is " << v.size() << std::endl;
  }

编辑:

示例可能被错误选择,因为它是一个“糟糕的优化”,正如许多人所报告的那样。但可能存在通过引用检索向量并且问题仍然存在的情况。例如:

bool hasData( std::vector<int>& retrievedData );

void splitVector( const std::vector<int>& originalVector,
                  std::vector<int>& part1,
                  std::vector<int>& part2 );

...

无单权。但是想到stringstreamclass这个对象可以参考。打算重新使用此 class 的对象的人需要在重新使用该对象之前进行清理。创建对象的人通常负责删除该对象。

Now, let's say I want to change getVector to avoid useless object copies and optimize speed and memory usage. So getVector will now get a reference to the vector to be filled.

有些事情你应该考虑一下:

  • 在您的第一个示例中没有副本,因为 RVO/NRVO 编译器优化。
  • 如果您使用 C++11,所有 STL 容器都有移动构造函数。所以,在这种情况下永远不会被复制。
  • 如果您更愿意接收推荐信,没有其他(正确的)方法只能在来电方清除。
  • 您可以在 getVector 函数的开头使用 vector::reserve 稍微优化此代码段。

在您的第一个示例中,该函数称为 "getVector",这适合描述该函数的作用。正如预期的那样,它 returns 具有一定数量元素的向量。

第二个例子不一样。该函数仍称为 "getVector" 但现在您想清除向量。它不再描述该函数,因为您将一个向量传递给它并用它做一些事情。它更像是 "takeAVectorClearItAndInitializeIt"。您可能不想这样命名函数或在您的代码中使用它。 (当然我有点夸大了,但你明白了)

为了使一个功能易于理解,它应该有一个明确的功能,而不是在后台做隐藏的事情(比如清除客户端的向量)。如果我要使用你的函数,我可能会搞不清楚为什么我的向量刚刚被清除。

通常是客户端代码负责管理自己的变量。即使客户端代码调用一个函数来清除向量,责任仍然在客户端代码中,用于清除其向量的意图。在您的情况下,您从客户端代码中知道 "getVector" 函数的作用,因为您同时创建了它们,所以它不会让您感到困惑,但是当您编写只需要两个函数之一的代码时(清除或初始化),您可能会进行一些更改,这些更改会影响使用此函数的所有代码并产生更多问题。


关于更具体地回答您的问题。

如果您不想复制传递引用的向量,那很好,但可以将函数命名为更具描述性的名称,例如 "initializeVector",这样可以更清楚地说明您在做什么。

尽管如此,您通常应该从客户端代码调用 'clear',因此在遇到性能问题之前,您的第一个示例会更好。 (清除是隐式的,因为你得到一个不同的向量)

都不是。 相反,您应该重新考虑您的设计决策。您为消除(可能是理论上的)性能问题而执行的操作 - 我从您的措辞中读到了这一点

avoid useless object copies and optimize speed and memory usage

替换两个具体问题:

  1. 这个函数的性能瓶颈是什么?
  2. 出于性能原因,我应该避免 std::vector 吗?

相对容易回答,抽象而困难,没有真正的答案,但它会给您(和我们)带来严重的头痛:您的 "optimization" 实际上会导致性能命中.