未使用智能指针或原始指针调用析构函数
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() = default
或 virtual ~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);
,现在您的代码不再显式调用 new
和 delete
,这很好习惯进入。但是你的代码被标记为 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 中,然后在两个分支上调用它。我在传递临时水果时使用了右值引用。
从
在此代码中,根本不会调用 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() = default
或 virtual ~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);
,现在您的代码不再显式调用 new
和 delete
,这很好习惯进入。但是你的代码被标记为 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 中,然后在两个分支上调用它。我在传递临时水果时使用了右值引用。