std::vector<T>::resize( n, val ) 是否足以进行初始化?
Is std::vector<T>::resize( n, val ) sufficient for initialisation?
这是一个特定于 C++11 的问题。
假设我已经使用了一个向量 std::vector<T> v
,并且我想将它的大小调整为 n
个元素,并使用现有值 T val
进行了初始化。 (典型用例:向量是被回收实例的成员)。
以下几种方式有哪些pros/cons,哪种方式效率最高?
1) std::vector<T>::resize( n, val )
是否足以初始化?
v.clear();
v.resize( n, val );
2) 如果不是,那么我假设以下是正确的?
v.clear();
v.resize(n);
std::fill( v.begin(), v.end(), val );
3) 交换如何?
v.swap( std::vector<T>( n, val ) );
(1) 就够了。 (3) 也有效。不同之处在于,如果新大小小于当前大小,(1) 不会释放内存。 (3) 总是分配新的内存块并删除旧的。
(2) 通常比 (1) 慢,因为它首先默认构造元素,然后分配它们。如果 T
不是默认构造的,它甚至可能无法编译。
让我们对其进行分解以显示它们之间的差异。我将使用 n 作为新尺寸,m 作为旧尺寸。
1.
v.clear();//keeps the same buffer, but calls the destructor on all the values
v.resize(n, val);//only makes a new buffer if the value is bigger, does no moves.
2.
v.clear();//keeps the same buffer, but calls the destructor on all the values
v.resize(n);//initializes all the values to default
std::fill( v.begin(), v.end(), val );//initializes all the values again to the new value
3.
v.swap( std::vector<T>( n, val ) );//calls destructor on all values in v, cannot reuse the buffer, initializes all the values to the new value
差异是微妙的,但却是真实的。 1 可以(但不保证)重用缓冲区,这样可以节省内存开销。 2 与 1 相同,但进行双重重新初始化。 3 与 1 相同,但不能重复使用缓冲区。
我个人认为,在大多数情况下,三者之间的差异太细微,无关紧要,1 是最易读的。
(4)
std::fill(v.begin(), std::min(v.begin() + n, v.end()), val);
v.resize(n, val);
如果 T 有合适的赋值行为,至少比构造一个新的更便宜,那么使用 (4)。 T = int(赋值和构造相同)和 T = std::string(赋值比构造更快,因为它可以使用现有缓冲区)就是这种情况。
如果 T 具有相同的赋值和构造成本(例如 T = int),则 (1) 也可以用于清晰度而不损失性能。
如果 T 不能被赋值,或者由于某种原因,赋值比构造(罕见)成本更高,那么使用 (1)
(1) 可以通过使用 v.assign(n, val);
来简化(感谢@Casey)
我不知道 (4) 使用 assign
在性能方面是否相同。我不知道 assign 是否会(具有讽刺意味的是,给定名称)将新元素分配给现有元素,或者重新构造它们。
编辑:对 (4) 的可能改进,我还没有测试过。它可以避免向量容量变化期间copying/moving的开销。
if (n <= v.capacity())
{
std::fill(v.begin(), std::min(v.begin() + n, v.end()), val);
v.resize(n, val);
}
else
{
v.assign(n, val);
}
为什么不使用专为这项工作设计的界面?
v.assign(n, val);
这是一个特定于 C++11 的问题。
假设我已经使用了一个向量 std::vector<T> v
,并且我想将它的大小调整为 n
个元素,并使用现有值 T val
进行了初始化。 (典型用例:向量是被回收实例的成员)。
以下几种方式有哪些pros/cons,哪种方式效率最高?
1) std::vector<T>::resize( n, val )
是否足以初始化?
v.clear();
v.resize( n, val );
2) 如果不是,那么我假设以下是正确的?
v.clear();
v.resize(n);
std::fill( v.begin(), v.end(), val );
3) 交换如何?
v.swap( std::vector<T>( n, val ) );
(1) 就够了。 (3) 也有效。不同之处在于,如果新大小小于当前大小,(1) 不会释放内存。 (3) 总是分配新的内存块并删除旧的。
(2) 通常比 (1) 慢,因为它首先默认构造元素,然后分配它们。如果 T
不是默认构造的,它甚至可能无法编译。
让我们对其进行分解以显示它们之间的差异。我将使用 n 作为新尺寸,m 作为旧尺寸。
1.
v.clear();//keeps the same buffer, but calls the destructor on all the values
v.resize(n, val);//only makes a new buffer if the value is bigger, does no moves.
2.
v.clear();//keeps the same buffer, but calls the destructor on all the values
v.resize(n);//initializes all the values to default
std::fill( v.begin(), v.end(), val );//initializes all the values again to the new value
3.
v.swap( std::vector<T>( n, val ) );//calls destructor on all values in v, cannot reuse the buffer, initializes all the values to the new value
差异是微妙的,但却是真实的。 1 可以(但不保证)重用缓冲区,这样可以节省内存开销。 2 与 1 相同,但进行双重重新初始化。 3 与 1 相同,但不能重复使用缓冲区。
我个人认为,在大多数情况下,三者之间的差异太细微,无关紧要,1 是最易读的。
(4)
std::fill(v.begin(), std::min(v.begin() + n, v.end()), val);
v.resize(n, val);
如果 T 有合适的赋值行为,至少比构造一个新的更便宜,那么使用 (4)。 T = int(赋值和构造相同)和 T = std::string(赋值比构造更快,因为它可以使用现有缓冲区)就是这种情况。
如果 T 具有相同的赋值和构造成本(例如 T = int),则 (1) 也可以用于清晰度而不损失性能。
如果 T 不能被赋值,或者由于某种原因,赋值比构造(罕见)成本更高,那么使用 (1)
(1) 可以通过使用 v.assign(n, val);
来简化(感谢@Casey)
我不知道 (4) 使用 assign
在性能方面是否相同。我不知道 assign 是否会(具有讽刺意味的是,给定名称)将新元素分配给现有元素,或者重新构造它们。
编辑:对 (4) 的可能改进,我还没有测试过。它可以避免向量容量变化期间copying/moving的开销。
if (n <= v.capacity())
{
std::fill(v.begin(), std::min(v.begin() + n, v.end()), val);
v.resize(n, val);
}
else
{
v.assign(n, val);
}
为什么不使用专为这项工作设计的界面?
v.assign(n, val);