简体   繁体   English

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

[英]Why does this union structure cause a memory leak?

I defined a union structure that can switch between a string pointer or a long (this is due to alignment issues). 我定义了一个可以在字符串指针或长指针之间切换的并集结构(这是由于对齐问题所致)。 The structure itself looks as follows. 结构本身如下所示。

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);}

Using valgrind I have determined that this data structure, as is, has a memory leak. 使用valgrind,我确定此数据结构本身存在内存泄漏。

The way to verify that this structure is the cause of the memory leak (ie the reason why I am sure this is the cause and not something else) was to overload all operators currently being used (=, <<, >>) and have two versions of the data structure. 验证此结构是导致内存泄漏的原因的方法(即,我确定这是原因而不是其他原因的原因)是重载当前正在使用的所有运算符(=,<<,>>),并具有数据结构的两个版本。 the first is the one you see above using the union. 第一个是您在上面使用联合会看到的那个。 the second simply has a string and a long as 2 separate fields in TextureID. 第二个在TextureID中仅包含一个字符串和一个长为2个单独的字段。

With the second implementation (the one not using pointers) there are no memory leaks. 在第二种实现中(一种不使用指针的实现),没有内存泄漏。

I am aware this can cause a segmentation fault if the Tag is set to a long.That is not the issue, the issue is that somehow, despite there being an explicite call to delete() the allocated memory is not getting deleted (currently nothing in the program ever sets the tag value to a long so no seg fault occurs either). 我知道如果将Tag设置为long可能会导致分段错误。这不是问题,问题在于,尽管显式调用了delete(),但分配的内存并未被删除(当前没有任何内容)在程序中,请将标签值设置为长值,这样也不会发生段错误。

EDIT: 编辑:

It was requested that I provide proof of a memory leak so here it is: 有人要求我提供内存泄漏的证明,所以这里是:

在此处输入图片说明

This version does not cause a memory leak: 此版本不会导致内存泄漏:

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;}

Your problem in your first code sample is almost certainly in 您在第一个代码示例中遇到的问题几乎可以肯定是

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

This ASSUMES that tag.path can be deleted. 可以删除该tag.path的tag.path It will cause undefined behaviour if that isn't true - for example, if type == H_LONG . 如果不是这样,则会导致未定义的行为-例如,如果type == H_LONG

While it is debatable whether that genuinely causes a leak, symptoms of undefined behaviour can be anything, including spurious reports of a memory leak from tools like valgrind. 尽管是否真正导致泄漏尚有争议,但不确定行为的症状可以是任何东西,包括来自valgrind之类的工具泄漏内存的虚假报告。

In any event, a simple fix would be to change this operator to check if(type == H_STRING) before doing delete tag.path . 无论如何,一个简单的解决方法是更改​​此运算符以在执行delete tag.path之前检查if(type == H_STRING)

The second example does not cause a leak, since the struct contains the members separately, and the compiler, by default, will ensure the destructor cleans all members up appropriately (invoking destructors, etc). 第二个示例不会引起泄漏,因为该struct单独包含成员,并且默认情况下,编译器将确保析构函数适当清理所有成员(调用析构函数等)。

As noted by PaulMcKenzie in comments, the second example has other issues that can also cause undefined behaviour (albeit, probably not a memory leak in practice). 正如PaulMcKenzie在评论中指出的那样,第二个示例还有其他问题,这些问题也可能导致未定义的行为(尽管实际上可能不是内存泄漏)。 I'll leave those issues alone - that's going beyond the question asked. 我将不理这些问题-超出了提出的问题。

This looks wrong: 这看起来是错误的:

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

tag.path is only valid if it was set (ie if you set tag.id its not valid). tag.path仅在已设置的情况下才有效(即,如果将tag.id设置为无效)。 So you need to make sure that you only call delete if tag.path was set. 因此,您需要确保仅在设置了tag.path下才调用delete。

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM