STL 向量:移动向量的所有元素

Posted

技术标签:

【中文标题】STL 向量:移动向量的所有元素【英文标题】:STL vector: Moving all elements of a vector 【发布时间】:2012-09-18 18:28:11 【问题描述】:

我有两个 STL 向量 AB,我想清除 A 的所有元素并将 B 的所有元素移动到 A,然后清除 B。简单地说,我想这样做:

std::vector<MyClass> A;
std::vector<MyClass> B;
....
A = B;
B.clear();

由于B 可能很长,所以需要k*O(N) 来执行此操作,其中k 是一个常数,Nmax(size_of(A), size_of(B))。我想知道是否有更有效的方法来做到这一点。我能想到的一件事是将AB 定义为指针,然后在恒定时间内复制指针并清除B

【问题讨论】:

【参考方案1】:

使用C++11,就这么简单:

A = std::move(B);

现在A 包含以前由B 持有的元素,而B 现在为空。这避免了复制:内部表示只是从B 移动到A,所以这是O(1) 解决方案。

对于 C++03,正如 Prætorian 所说,您可以交换向量。 std::swap 函数有一个特化,它将std::vectors 作为其参数。这有效地交换了内部表示,因此您最终避免创建它们所持有的元素的副本。此函数也适用于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 的组合来完成。首先交换 AB 上半年。然后swap 一个空的std::vector&lt;&gt;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++ STL 向量实现问题

stl

C++ 将元素从一个向量移动到另一个向量

是否有一个函数可以从向量中删除一个元素而不在 c++ stdlib 中移动它?

首先创建字符串然后通过移动语义将其添加到向量或在向量中创建元素是不是具有内存效率?

我是不是保证在移动向量后指向 std::vector 元素的指针有效?