繁体   English   中英

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

[英]STL vector: Moving all elements of a vector

我有两个STL向量AB ,我想清除的所有元素A和移动的所有元素BA然后清除出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

使用C ++ 11,它很简单:

A = std::move(B);

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

对于C ++ 03,正如Prætorian所说,你可以交换向量。 std::swap函数有一个特化,它以std::vector s作为参数。 这有效地交换了内部表示,因此您最终避免创建它们所持有的元素的副本。 此功能也适用于O(1)复杂度。

如果你有一个C ++ 11编译器,你可以将B移动到A

A = std::move(B);

如果您正在使用较旧的编译器,只需swap这两个

A.swap(B);

在这两种情况下,唯一的O(N)操作将清除A的内容。 在第一种情况下,清除将在赋值本身期间完成,而在第二种情况下,它将在B超出范围时发生(因为内容被交换)。

我有两个STL向量A和B,我想清除A的所有元素并将B的所有元素移动到A然后清除B.

这可以通过swap的组合来完成。 首先交换上半场的AB 然后用B swap空的std::vector<>或调用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);

只需在向量上调用清除将花费o(1)时间,因为clear将无效,如果你真的想在将其分配给A之后清除B,你可以执行以下操作

A.swap(B);
{
    std::Vector<..> C;
    c.swap(B);
}

交换功能执行此操作。

#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:

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

如果你不能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具有相同数量的元素,但它们都处于不确定状态(即,它们可以分配或销毁,但不能读取)。

我没有回复评论,但我想提一下: https: //en.cppreference.com/w/cpp/container/vector/operator%3D void.pointer是对的。 特别是...

2)移动赋值运算符。 使用移动语义替换其他内容(即其他数据从其他数据移动到此容器中)。 其他人之后处于有效但未指明的状态。

因此,Praetorian的答案每个标准都是错误的。 但是,对于MSVC至少,这已经足够好了,因为无论如何实现都会清除列表(大多数情况下都是如此)。

有趣的是,由于我们声明了一个移动构造函数,因此不会声明隐式移动赋值运算符。 因此,我们“知道”std :: vector必须声明一个移动赋值运算符。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM