在 std::list<shared_ptr> 的 C++14 初始化列表中间抛出时,GCC(但不是 Clang)下的内存泄漏
memory leak under GCC (but not Clang) when throwing in the middle of a C++14 initializer list for std::list<shared_ptr>
考虑以下程序:
#include <stdexcept>
#include <stdio.h>
#include <memory>
#include <list>
class Foo {
public:
Foo(){
if (s_ct==0) {throw std::bad_alloc();}
--s_ct;
fprintf(stderr, "ctor %p\n", this);
}
~Foo(){
fprintf(stderr, "dtor %p\n", this);
}
private:
static int s_ct;
};
int Foo::s_ct = 2;
int main(){
try {
std::list<std::shared_ptr<Foo>> l = {
std::make_shared<Foo>(),
std::make_shared<Foo>(),
std::make_shared<Foo>()
};
} catch (std::bad_alloc&) {
fprintf(stderr, "caught exception.\n");
}
fprintf(stderr, "done.\n");
return 0;
}
这样编译:
[little:~] $ g++ --version
g++ (Ubuntu 5.4.0-6ubuntu1~16.04.2) 5.4.0 20160609
Copyright (C) 2015 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
[little:~] $ g++ --std=c++14 -o list_init list_init.cc
[little:~] $
输出为:
[little:~] $ ./list_init
ctor 0x1294c30
ctor 0x1294c50
caught exception.
done.
[little:~] $
注意没有调用析构函数。 Valgrind 也正确地抱怨了泄漏。
这似乎违反了 std::make_shared
的一个关键目的——即,如果语句中的另一个表达式抛出异常,共享对象将被正确销毁,因为它被完全构造的共享指针对象包装.
Clang 在这里做我想做的事:
[little:~] $ clang++ --version
clang version 3.8.0-2ubuntu4 (tags/RELEASE_380/final)
Target: x86_64-pc-linux-gnu
Thread model: posix
InstalledDir: /usr/bin
[little:~] $ clang++ --std=c++14 -o foo list_init.cc
[little:~] $ ./foo
ctor 0x1dfec30
ctor 0x1dfec50
dtor 0x1dfec50
dtor 0x1dfec30
caught exception.
done.
[little:~] $
这是 GCC 错误,还是我需要修复我的程序?
将我的评论变成答案,这样您就可以将问题标记为已回答。
这看起来是一个 gcc 错误
Bug 66139 - destructor not called for members of partially constructed anonymous struct/array
请特别注意最后两个使用 std::initializer_list
来说明问题的测试用例。
考虑以下程序:
#include <stdexcept>
#include <stdio.h>
#include <memory>
#include <list>
class Foo {
public:
Foo(){
if (s_ct==0) {throw std::bad_alloc();}
--s_ct;
fprintf(stderr, "ctor %p\n", this);
}
~Foo(){
fprintf(stderr, "dtor %p\n", this);
}
private:
static int s_ct;
};
int Foo::s_ct = 2;
int main(){
try {
std::list<std::shared_ptr<Foo>> l = {
std::make_shared<Foo>(),
std::make_shared<Foo>(),
std::make_shared<Foo>()
};
} catch (std::bad_alloc&) {
fprintf(stderr, "caught exception.\n");
}
fprintf(stderr, "done.\n");
return 0;
}
这样编译:
[little:~] $ g++ --version
g++ (Ubuntu 5.4.0-6ubuntu1~16.04.2) 5.4.0 20160609
Copyright (C) 2015 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
[little:~] $ g++ --std=c++14 -o list_init list_init.cc
[little:~] $
输出为:
[little:~] $ ./list_init
ctor 0x1294c30
ctor 0x1294c50
caught exception.
done.
[little:~] $
注意没有调用析构函数。 Valgrind 也正确地抱怨了泄漏。
这似乎违反了 std::make_shared
的一个关键目的——即,如果语句中的另一个表达式抛出异常,共享对象将被正确销毁,因为它被完全构造的共享指针对象包装.
Clang 在这里做我想做的事:
[little:~] $ clang++ --version
clang version 3.8.0-2ubuntu4 (tags/RELEASE_380/final)
Target: x86_64-pc-linux-gnu
Thread model: posix
InstalledDir: /usr/bin
[little:~] $ clang++ --std=c++14 -o foo list_init.cc
[little:~] $ ./foo
ctor 0x1dfec30
ctor 0x1dfec50
dtor 0x1dfec50
dtor 0x1dfec30
caught exception.
done.
[little:~] $
这是 GCC 错误,还是我需要修复我的程序?
将我的评论变成答案,这样您就可以将问题标记为已回答。
这看起来是一个 gcc 错误
Bug 66139 - destructor not called for members of partially constructed anonymous struct/array
请特别注意最后两个使用 std::initializer_list
来说明问题的测试用例。