首页 > 解决方案 > 带有标记调度的 const 成员构造函数中的内存泄漏

问题描述

我有一个类Bar,它有一个类型的成员Foo。类Foo应该只在某些固定和恒定的状态下构造,这些状态是基于 a 来区分的Tag。由于我不想Foo在任何其他状态下构造,我将其构造函数设为私有并实现了 factory FooFactory

在 的Bar构造函数的初始化列表中,我调用函数,它返回基于make_Foo的正确实例。FooTag

#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时不是由工厂创建的,但仍然初始化为 unknown和 empty 。这是在这种情况下泄漏的内存吗?抛出异常时如何避免这种情况发生?Tag::Cbar.fooTagstd::string

附言。实际上Foo是一个非类型模板类,而对于Tag::C,使用了另一个模板参数。这就是为什么我需要标签调度。

标签: c++memory-leaksconstructorc++14

解决方案


由于程序过早终止,可能存在“内存泄漏”。为了执行所有析构函数并释放内存,您不能允许异常转义main函数(或以其他方式调用std::abort或以其他方式导致引发终端信号)。

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


推荐阅读