为什么这个联合结构会导致内存泄漏?

时间:2018-07-14 06:14:15

标签: c++ pointers memory memory-leaks unions

我定义了一个可以在字符串指针或长指针之间切换的并集结构(这是由于对齐问题所致)。结构本身如下所示。

enum {H_STRING, H_LONG};
union Tag
{
    std::string *path;
    long id;
};

struct TextureID
{
    Tag tag;
    int type=H_STRING;

    TextureID()
    {
        type = H_STRING;
        tag.path = new std::string("");
    }
    TextureID(const TextureID& TID)
    {
        type = TID.type;
        if(type==H_STRING)
            tag.path = new std::string(*(TID.tag.path));
        else
            tag.id = TID.tag.id;
    }
    ~TextureID()
    {
        delete(tag.path);

    }

    TextureID& operator= (std::string str)
    {delete(tag.path); tag.path = new std::string(str); type=H_STRING; return *this;}
    TextureID& operator= (long val)
    { if(type==H_STRING) delete(tag.path); tag.id = val; type=H_LONG; return *this;}

    operator std::string&()
    {
        if(type == H_STRING)
        {
            return *(tag.path);
        }
    }
};

std::istream inline &operator>> (std::istream& is, TextureID& TID)
{is >> *(TID.tag.path); TID.type = H_STRING; return is;}

std::ostream inline &operator<< (std::ostream& os, TextureID& TID)
{return os << *(TID.tag.path);}

使用valgrind我已经确定该数据结构本身存在内存泄漏。

验证此结构是导致内存泄漏的原因的方法(即,我确定这是原因而不是其他原因的原因)是重载当前正在使用的所有运算符(=,<<,>> ),并具有两个版本的数据结构。第一个是您在上面使用联合会看到的那个。第二个在TextureID中仅包含一个字符串和一个长达2个单独的字段。

在第二种实现中(一种不使用指针的实现),没有内存泄漏。

我知道如果将Tag设置为long可能会导致分段错误,这不是问题,问题在于,尽管显式调用了delete(),但分配的内存并未被删除(当前程序中没有任何内容将标记值设置为很长,因此也不会发生段错误)。

编辑:

有人要求我提供内存泄漏的证明,所以这里是:

enter image description here

此版本不会导致内存泄漏:

enum {H_STRING, H_LONG};

struct TextureID
{
    std::string path;
    long ID;
    int type=H_STRING;

    TextureID& operator= (std::string str)
    {path = str;}
    TextureID& operator= (long val)
    {ID = val;}

    operator std::string&()
    {
        if(type == H_STRING)
        {
            return (path);
        }
    }
};

std::istream inline &operator>> (std::istream& is, TextureID& TID)
{is >> TID.path; return is;}

std::ostream inline &operator<< (std::ostream& os, TextureID& TID)
{return os << TID.path;}

2 个答案:

答案 0 :(得分:1)

您的第一个代码示例中的问题几乎可以肯定存在

TextureID& operator= (std::string str)
{delete(tag.path); tag.path = new std::string(str); type=H_STRING; return *this;}

此假设可以删除tag.path。如果不是这样,则会导致未定义的行为-例如,type == H_LONG

虽然是否真正导致泄漏值得商bat,但不确定行为的症状可以是任何东西,包括来自valgrind等工具的内存泄漏的虚假报告。

无论如何,一个简单的解决方法是更改​​此运算符以在执行if(type == H_STRING)之前检查delete tag.path

第二个示例不会导致泄漏,因为struct分别包含成员,并且编译器默认情况下将确保析构函数适当地清理所有成员(调用析构函数等)。

正如PaulMcKenzie在评论中指出的那样,第二个示例还存在其他一些问题,这些问题也可能导致未定义的行为(尽管实际上可能不是内存泄漏)。我将不理会这些问题-超出了提出的问题。

答案 1 :(得分:0)

这看起来是错误的:

~TextureID()
{
    delete(tag.path);
}

tag.path仅在已设置的情况下才有效(即,如果将tag.id设置为无效)。因此,您需要确保仅在设置了tag.path的情况下才调用delete。

~TextureID()
{
    if (type == H_STRING) {
        delete(tag.path);
    }
}