在使用C++14的std::list<shared_ptr>初始化列表时,如果在中间抛出异常,使用GCC编译器会出现内存泄漏问题(但使用Clang编译器不会)。

22

考虑以下程序:

#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的bug,还是我需要修复我的程序?


16
似乎是一个gcc bug:"部分构造的匿名结构体/数组成员未调用析构函数"。 - Steve Lorimer
1个回答

12

将我的评论转化为答案,以便您可以标记问题已得到解答。

这看起来像是gcc的一个bug

Bug 66139 - 局部构造匿名结构/数组的成员不调用析构函数

请特别注意最后两个测试用例,它们使用std::initializer_list说明了问题。


它是否与部分对象(在构造函数引发异常时创建)不同,并且析构函数不会在后续调用? - Vishwadeep Singh

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接