【问题标题】:Destructor not being called with smart or raw pointer [closed]没有使用智能或原始指针调用析构函数[关闭]
【发布时间】:2015-01-28 13:55:41
【问题描述】:

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;
}

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

【问题讨论】:

  • Fruit 有虚拟析构函数吗?否则你有未定义的行为(可能表现为未能调用正确的析构函数)。
  • @Deduplicator:甚至没有指针: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); } }
  • 所有课程是否包括Fruit...我知道愚蠢的问题,请耐心等待
  • 没有完整的类定义,真的不可能说出任何事情。
  • 这个问题离题外话只差一票,请添加必要的信息让我们回答,这意味着至少Fruit的析构函数声明,最好是MCVE

标签: c++ c++11


【解决方案1】:

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

但这不是您的代码应有的样子。只是工作,好吧,还不够好。

您可以对代码进行许多改进。作为第一个改进,我们将使用 unique_ptr:(如上面 cmets 中提到的 @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&lt;Blah&gt;(whatever);,现在您的代码不会显式调用newdelete,这是一个很好的习惯。但是您的代码标记为 C++11,所以我将 C++11 版本保留在上面。

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

避免使用免费商店的一个简单方法是:(从上面 cmets 中的@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 每当我读到它。这是一个加分项。

【讨论】:

  • 不,所有的析构函数都是虚拟的。我知道我可以用你的 lambda 解决这个问题,但我现在很好奇为什么析构函数不起作用。
  • @lapinrigolo 因为您正在传递临时对象,并且临时对象不绑定到左值引用。所以我使用了右值引用。如果const&amp; 可以工作(即,如果uv_runconst loop 可以工作),您可以使用它。在 C++14 中,我可能只使用转发引用 (auto&amp;&amp;)。 -- 所以,Fruit 有一个显式析构函数,它是virtual?发布 Fruit 的实际析构函数。
  • virtual ~Fruit(); - 实现为空。
猜你喜欢
  • 2014-07-25
  • 1970-01-01
  • 2016-03-20
  • 1970-01-01
  • 2011-04-06
  • 2012-04-17
  • 2018-05-13
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多