STL 向量:移动向量的所有元素
Posted
技术标签:
【中文标题】STL 向量:移动向量的所有元素【英文标题】:STL vector: Moving all elements of a vector 【发布时间】:2012-09-18 18:28:11 【问题描述】:我有两个 STL 向量 A
和 B
,我想清除 A
的所有元素并将 B
的所有元素移动到 A
,然后清除 B
。简单地说,我想这样做:
std::vector<MyClass> A;
std::vector<MyClass> B;
....
A = B;
B.clear();
由于B
可能很长,所以需要k*O(N)
来执行此操作,其中k
是一个常数,N
是max(size_of(A), size_of(B))
。我想知道是否有更有效的方法来做到这一点。我能想到的一件事是将A
和B
定义为指针,然后在恒定时间内复制指针并清除B
。
【问题讨论】:
【参考方案1】:使用C++11,就这么简单:
A = std::move(B);
现在A
包含以前由B
持有的元素,而B
现在为空。这避免了复制:内部表示只是从B
移动到A
,所以这是O(1)
解决方案。
对于 C++03,正如 Prætorian 所说,您可以交换向量。 std::swap
函数有一个特化,它将std::vector
s 作为其参数。这有效地交换了内部表示,因此您最终避免创建它们所持有的元素的副本。此函数也适用于O(1)
复杂度。
【讨论】:
你这样做之后,你能不能调用B.clear()
,然后继续使用B作为一个空向量,或者B
的任何使用现在未定义?
B
那时已经是空的,所以 B.clear()
是空操作。是的,你仍然可以使用向量,它只是一个空向量。
仅供参考,B 不是空的,它处于有效但未指定的状态。移动后B
的大小不保证为0
,按照标准。这就是为什么swap()
仍然有用并且 move 不能替代它的原因:它保证只要A
为空,B
现在也将为空。
@void.pointer cppreference 的文档在这种情况下确实与您相矛盾。 std::vector 的移动构造函数定义包括:“使用移动语义用 other 的内容构造容器。分配器是从属于 other 的分配器通过移动构造获得的。移动后,确保 other 为空()”@ 987654321@
@steeldotme 你错了。您已经链接了移动构造函数文档。这个答案是使用 move assignment.【参考方案2】:
如果您有 C++11 编译器,您可以将 B
移动到 A
。
A = std::move(B);
如果您使用的是较旧的编译器,只需 swap
这两个
A.swap(B);
在这两种情况下,唯一的 O(N) 操作将清除A
的内容。在第一种情况下,清除将在分配过程中完成,而在第二种情况下,当B
超出范围时(因为交换了内容)。
【讨论】:
【参考方案3】:我有两个 STL 向量 A 和 B,我想清除 A 的所有元素并将 B 的所有元素移动到 A,然后清除 B。
这可以通过swap
的组合来完成。首先交换 A
和 B
上半年。然后swap
一个空的std::vector<>
和B
或致电clear()
。不同的是clear()
不会释放内存,只会销毁对象:
std::vector<int> a, b; // initialize them somehow
swap(a,b);
// clear b without releasing the memory:
std::size_t capacity = b.capacity();
b.clear();
assert(b.capacity()==capacity);
// or release the memory
std::vector<int>().swap(b);
assert(b.capacity()==0);
【讨论】:
【参考方案4】:只需在向量上调用 clear 将花费 o(1) 时间,因为 clear 不会做任何事情, 如果您真的想在将 B 分配给 A 后清除它,您可以执行以下操作
A.swap(B);
std::Vector<..> C;
c.swap(B);
【讨论】:
【参考方案5】:std::move 工作正常。这是相同的示例代码
vector<int> v1 = 1,2,3,10,20,30,100,200,300,999;
vector<int> v2;
cout << "Size of v1 before move = " << v1.size() << endl;
cout << "Capacity of v1 before move = " << v1.capacity() << endl;
v2 = std::move(v1);
cout << "Size of v2 after move = " << v2.size() << endl;
cout << "Capacity of v2 after move = " << v2.capacity() << endl;
cout << "Size of v1 after move = " << v1.size() << endl;
cout << "Capacity of v1 after move = " << v1.capacity() << endl;
-----------Output-------------------------
Size of v1 before move = 10
Capacity of v1 before move = 10
Size of v2 after move = 10
Capacity of v2 after move = 10
Size of v1 after move = 0
Capacity of v1 after move = 0
【讨论】:
【参考方案6】:交换函数会这样做。
#include <iostream>
#include <iterator>
#include <vector>
int main(int argc, char* argv)
std::vector<int> A;
std::vector<int> B;
for (int i = 0; i < 10; ++i)
B.push_back(i);
std::cout << "Before swap\n";
std::cout << "A:";
std::copy(A.begin(), A.end(), std::ostream_iterator<int>(std::cout, " "));
std::cout << "\nB:";
std::copy(B.begin(), B.end(), std::ostream_iterator<int>(std::cout, " "));
std::cout << "\n";
A.swap(B);
B.clear();
std::cout << "After swap\n";
std::cout << "A:";
std::copy(A.begin(), A.end(), std::ostream_iterator<int>(std::cout, " "));
std::cout << "\nB:";
std::copy(B.begin(), B.end(), std::ostream_iterator<int>(std::cout, " "));
std::cout << "\n";
输出
Before swap
A:
B:0 1 2 3 4 5 6 7 8 9
After swap
A:0 1 2 3 4 5 6 7 8 9
B:
【讨论】:
你不能使用clear
。函数clear
只是删除元素,但分配给这个向量的内存并没有释放。【参考方案7】:
如果你不能 std::move 或 std::swap 向量(例如,因为 A 和 B 是相关的但不同的类型,可能只有 const 不同),你可以这样做:
std::vector<MyClass> A;
std::vector<const MyClass> B;
// ...
for( auto& a : A )
B.emplace_back( std::move( a ) );
请注意,这使 A 具有相同数量的元素,但它们都处于不确定状态(即,它们可以被分配或破坏,但不能被读取)。
【讨论】:
【参考方案8】:我没有代表发表评论,但我想提一下:https://en.cppreference.com/w/cpp/container/vector/operator%3Dvoid.pointer 是对的。特别是……
2) 移动赋值运算符。使用移动语义将内容替换为 other 的内容(即 other 中的数据从 other 移动到此容器中)。 other 之后处于有效但未指定的状态。
因此,根据标准,Praetorian 的答案是错误的。但是,至少对于 MSVC 来说,已经足够好了,因为无论如何该实现都会清除列表(可能对大多数人来说都是如此)。
有趣的是,由于我们声明了移动构造函数,因此不会声明隐式移动赋值运算符。因此我们“知道” std::vector 必须声明一个移动赋值运算符。
【讨论】:
以上是关于STL 向量:移动向量的所有元素的主要内容,如果未能解决你的问题,请参考以下文章
是否有一个函数可以从向量中删除一个元素而不在 c++ stdlib 中移动它?