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

时间:2015-01-28 13:55:42

标签: c++ c++11

继续previous question

在这段代码中,Apple和Fruit的析构函数根本没有被调用。我在两者中都有std::cerr个语句,并且Apple中的一些清理代码无法运行。我认为调用删除就足够了?我正确地做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;
}

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

1 个答案:

答案 0 :(得分:6)

首先,您的直接问题几乎肯定是~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。这是一个加号。