谁应该清除通过引用检索的向量?
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
替换两个具体问题:
- 这个函数的性能瓶颈是什么?
- 出于性能原因,我应该避免
std::vector
吗?
相对容易回答,抽象而困难,没有真正的答案,但它会给您(和我们)带来严重的头痛:您的 "optimization" 实际上会导致性能命中.
假设我有这个非常简单的代码:
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
替换两个具体问题:
- 这个函数的性能瓶颈是什么?
- 出于性能原因,我应该避免
std::vector
吗?
相对容易回答,抽象而困难,没有真正的答案,但它会给您(和我们)带来严重的头痛:您的 "optimization" 实际上会导致性能命中.