遍历 unique_ptr 拥有的内存会出现段错误

traversing memory owned by a unique_ptr gives segfault

为了不用记住删除,我们使用unique_ptr来管理内存。 我们的印象是我们可以在内存中写入和读取,只是删除取决于智能指针。但是,以下代码在 i=7220 上崩溃并出现段错误。

怎么了?

#include <memory>
using namespace std;

int main() {
  const uint32_t n = 40000000;
  uint64_t*p = new uint64_t[n]; // this works, but we have to delete the memory or leak
  for (int i = 0; i < n; i++)
    p[i] = i;

  unique_ptr<uint64_t> mem = make_unique<uint64_t>(n);

  uint64_t* p1 = mem.get();
  for (int i = 0; i < n; i++) // this crashes at i=7220
    p1[i] = i;

  return 0;
}
unique_ptr<uint64_t> mem = make_unique<uint64_t>(n);

这会使用值 n.

动态分配 one uint64_t

你想要:

unique_ptr<uint64_t[]> mem = make_unique<uint64_t[]>(n);

这个特化分配了一个 array of uint64_t with n elements, and has an operator[] overload which makes below:

for (int i = 0; i < n; i++)
    mem[i] = i;

所以,没必要uint64_t* p1 = mem.get();

unique_ptr<uint64_t> 是指向单个 uint64_t 值的指针。为了让它存储一个数组,你需要使用 array of unknown bound 语法:

unique_ptr<uint64_t[]> mem = make_unique<uint64_t[]>(n);

或者只是

auto mem{make_unique<uint64_t[]>(n)};

请注意,此变体重载了运算符 [],因此无需创建中间原始指针 uint64_t* p1 = mem.get();

除了提到的其他答案的修复之外,您可能不想在这里使用make_uniquemake_unique 会将每个元素清零(4000 万个整数!),然后您会立即覆盖它们。您的选择是使用需要 C++20 的 make_unique_for_overwrite,或使用 new:

#include <cstdint>  // for uint64_t
#include <memory>   // for unique_ptr, unique_ptr<>::pointer
#include <numeric>  // for iota

int main() {
  auto const n = 40'000'000;
  auto const p = std::unique_ptr<std::uint64_t[]>{new std::uint64_t[n]};
  std::iota(p.get(), p.get() + n, 0);
}