未使用智能指针或原始指针调用析构函数

Destructor not being called with smart or raw pointer

继续:

在此代码中,根本不会调用 Apple 和 Fruit 的析构函数。我在两者中都有 std::cerr 语句,Apple 中有一些清理代码没有 运行。我认为调用 delete 就足够了吗?我做 RAII 正确吗?我还用 std::unique_ptr 和相同的结果替换了原始指针。

int32_t Fruit::frutificate(const Settings& settings) {
  Fruit *listener;
  if (settings.has_domain_socket()) {
    listener = new Apple(settings);
  } else {
    listener = new Orange(settings);
  }
  int r = uv_run(listener->loop, UV_RUN_DEFAULT);
  delete listener;
  return r;
}

更新: 所有 类 都有虚拟析构函数。

首先,您的直接问题几乎可以肯定是 ~Fruit() 不是 virtual。添加那个(virtual ~Fruit() = defaultvirtual ~Fruit() {}class Fruit),您的代码(如发布的那样)将神奇地开始工作。

然而,这不是您的代码应有的样子。只是工作,嗯,还不够好。

您可以对代码进行多项改进。作为第一个改进,我们将使用 unique_ptr:(如上面评论中提到的@Deduplicator)

int32_t Fruit::frutificate(const Settings& settings) {
  std::unique_ptr<Fruit> listener;
  if (settings.has_domain_socket()) {
    listener.reset( new Apple(settings) );
  } else {
    listener.reset( new Orange(settings) );
  }
  int r = uv_run(listener->loop, UV_RUN_DEFAULT);
  return r;
}

它使用 RAII 来确保监听器的生命周期是有界的。好多了,不再需要手动 delete(可能会因意外或异常而错过)。

在 C++14 中,.reset(new Blah(whatever)) 可以替换为 = std::make_unique<Blah>(whatever);,现在您的代码不再显式调用 newdelete,这很好习惯进入。但是你的代码被标记为 C++11,所以我将在上面保留 C++11 版本。

虽然这样更好,但我们可以做到最好。根本不需要使用免费存储(堆)。

一个避免使用免费商店的简单方法是:(在上面的评论中从@Jarod 那里偷来的)

int32_t Fruit::frutificate(const Settings& settings) {
  if (settings.has_domain_socket()) {
    return uv_run(Apple(settings).loop, UV_RUN_DEFAULT);
  } else {
    return uv_run(Orange(settings).loop, UV_RUN_DEFAULT);
  }
}

的缺点是会重复 uv_run 代码(因此会滋生错误)。我们可以用 lambda 来解决这个问题:

int32_t Fruit::frutificate(const Settings& settings) {
  auto fruit_the_uv = [&](Fruit&& fruit) {
    return uv_run(fruit.loop, UV_RUN_DEFAULT);
  };
  if (settings.has_domain_socket()) {
    return fruit_the_uv( Apple(settings) );
  } else {
    return fruit_the_uv( Orange(settings) );
  }
}

我们将公共代码提取到 lambda 中,然后在两个分支上调用它。我在传递临时水果时使用了右值引用。

此外,fruit_the_uv 每当我阅读它时都会让我想起一个 90s rap song。这是一个加号。