如何从向量<unique_ptr <Foo >>中移出unique_ptr?

Dou*_*oug 4 c++ vector unique-ptr

我想unique_ptr<Foo>移出一个vector<unique_ptr<Foo>>.考虑我的代码:

#include <vector>
#include <memory>
#include <iostream>

using namespace std;

class Foo {
public:
  int x;
  Foo(int x): x(x) {};
  ~Foo() {
    cout << "Destroy of id: " << x << "\n";
    x = -1;
  };
};

int main(int argc, char *argv[]) {
  auto foos = vector<unique_ptr<Foo>>();
  foos.push_back(unique_ptr<Foo>(new Foo(100)));
  foos.push_back(unique_ptr<Foo>(new Foo(101)));
  foos.push_back(unique_ptr<Foo>(new Foo(102)));

  // Print all
  cout << "Vector size: " << foos.size() << "\n";
  for (auto i = foos.begin(); i != foos.end(); ++i) {
    cout << (*i)->x << "\n";
  }

  // Move Foo(100) out of the vector
  {
    auto local = move(foos.at(0));
    cout << "Removed element: " << local->x << "\n";
  }

  // Print all! Fine right?
  cout << "Vector size: " << foos.size() << "\n";
  for (auto i = foos.begin(); i != foos.end(); ++i) {
    cout << (*i)->x << "\n";
  }

  return 0;
}
Run Code Online (Sandbox Code Playgroud)

我预计会产生:

Vector size: 3
100
101
102
Removed element: 100
Destroy of id: 100
Vector size: 2
101
102
Run Code Online (Sandbox Code Playgroud)

但相反,我得到了这个结果:

Vector size: 3
100
101
102
Removed element: 100
Destroy of id: 100
Vector size: 3
Segmentation fault: 11
Run Code Online (Sandbox Code Playgroud)

为什么我的矢量大小仍为3,为什么我会出现分段错误?我怎样才能得到我想要的结果?

Bar*_*rry 8

让我们将您的问题简化为:

vector<unique_ptr<Foo>> foos;
foos.push_back(unique_ptr<Foo>(new Foo(100)));
auto local = std::move(foos[0]);
std::cout << foos[0]->x << '\n';
Run Code Online (Sandbox Code Playgroud)

local通过移动创建后foos[0],foos[0]不再拥有指针的所有权.它是空的.取消引用它会变成未定义的行为,在您的情况下表现为分段错误.此时vector完全"完整",它包含一个空unique_ptr,并且等效于状态:

vector<unique_ptr<Foo>> foos(1);
Run Code Online (Sandbox Code Playgroud)

您应该在解除引用之前检查您是否unique_ptr拥有指针:

if (foos[0]) {
    // we wouldn't get here
    std::cout << foos[0]->x << '\n';
}
Run Code Online (Sandbox Code Playgroud)

或者,因为你想要强制执行vector只包含有效指针的不变量,作为移动操作的一部分,你应该只使用erase该元素:

auto local = std::move(foos[0]);
foos.erase(foos.begin());
// now foos is empty
Run Code Online (Sandbox Code Playgroud)