带有标记分派的const成员构造函数中的内存泄漏

时间:2018-12-13 12:46:23

标签: c++ memory-leaks constructor c++14

我有一个类Bar,它的成员类型为Foo。类Foo应该仅以某些固定和恒定状态构造,这些状态基于Tag进行区分。由于我不希望在任何其他状态下构造Foo,因此我将其构造函数设为私有,并实现了工厂FooFactory

Bar的构造函数的初始化程序列表中,我调用函数make_Foo,该函数根据Foo返回Tag的正确实例。

#include <stdexcept>
#include <string>
#include <iostream>

enum class Tag
{
    A,
    B,
    C
};

class Foo
{
public:
    friend class FooFactory;
    const Tag tag;
    const std::string string;
private:
    Foo(Tag tag, std::string string): 
    tag {tag}, string {string}
    {};
};

class FooFactory
{
public:
    static Foo A()
    {
        return Foo(Tag::A, {"This is string A"});
    }

    static Foo B()
    {
        return Foo(Tag::B, {"This is string A"});
    }
};

Foo make_Foo(Tag tag)
{
    switch(tag)
    {
        case Tag::A: return FooFactory::A();
        case Tag::B: return FooFactory::B();
        default: throw std::runtime_error("Called with invalid Tag.");
    }
}

class Bar
{
public:
    std::string another_string;
    const Foo foo;

    Bar(Tag tag, std::string another_string): 
    another_string {another_string}, foo {make_Foo(tag)}
    {};
};

int main()
{
    Tag tag = Tag::C;
    Bar bar(tag, "This is a string");
    std::cout << "bar constructed" << std::endl;
}

我希望在用Foo构造Tag::C时抛出一个异常,但未实现。上面的代码引发了此异常,但是Valgrind Memcheck表示在这种情况下存在内存泄漏。

在进一步调查中,我发现即使bar.foo并非由工厂创建的,Tag::C仍通过未知的bar.foo和空Tag。这是在这种情况下泄漏的内存吗?抛出异常时,如何避免这种情况发生?

ps。实际上,std::string是非类型的模板类,对于Foo,使用另一个模板参数。这就是为什么我需要标签分派。

1 个答案:

答案 0 :(得分:2)

由于程序过早终止,可能会出现“内存泄漏”。为了执行所有析构函数并释放内存,您必须不允许异常逸出main函数(或调用std::abort或引发终端信号)。

具有静态持续时间的对象在主返回后被销毁,并且如果终止进程,则不会清除静态对象。此类静态对象可能已分配了动态内存,如果静态对象未被破坏,则动态内存可能会泄漏。即使您不这样做,标准库也可能使用带有静态存储的对象。例如,std::cout对象。