为什么这个联合结构会导致内存泄漏?
Why does this union structure cause a memory leak?
我定义了一个联合结构,它可以在字符串指针或长整数之间切换(这是由于对齐问题)。结构本身如下所示。
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 确定这个数据结构存在内存泄漏。
验证此结构是内存泄漏原因的方法(即我确定这是原因而不是其他原因的原因)是重载当前正在使用的所有运算符(=、<<、 >>) 并且有两个版本的数据结构。第一个是您在上面看到的使用 union 的那个。第二个只是在 TextureID 中有一个字符串和一个 long 2 个单独的字段。
第二个实现(不使用指针的那个)没有内存泄漏。
我知道如果将标签设置为 long.That 这可能会导致分段错误不是问题,问题是不知何故,尽管有明确调用 delete() 分配的内存没有被删除(目前程序中没有任何东西将标签值设置为 long 所以也没有发生段错误)。
编辑:
有人要求我提供内存泄漏的证据,所以这里是:
此版本不会导致内存泄漏:
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;}
这看起来不对:
~TextureID()
{
delete(tag.path);
}
tag.path
仅在设置后有效(即如果设置 tag.id 则无效)。因此,您需要确保仅在设置了 tag.path
时才调用 delete。
~TextureID()
{
if (type == H_STRING) {
delete(tag.path);
}
}
您的第一个代码示例中的问题几乎肯定在
TextureID& operator= (std::string str)
{delete(tag.path); tag.path = new std::string(str); type=H_STRING; return *this;}
这假设 tag.path
可以被删除。如果不是这样,它将导致未定义的行为 - 例如,如果 type == H_LONG
.
虽然这是否真的会导致泄漏值得商榷,但未定义行为的症状可以是任何东西,包括来自 valgrind 等工具的内存泄漏的虚假报告。
无论如何,一个简单的解决方法是更改此运算符以在执行 delete tag.path
之前检查 if(type == H_STRING)
。
第二个示例不会导致泄漏,因为 struct
单独包含成员,默认情况下,编译器将确保析构函数正确清理所有成员(调用析构函数等)。
正如 PaulMcKenzie 在评论中指出的那样,第二个示例还有其他问题也可能导致未定义的行为(尽管在实践中可能不是内存泄漏)。我将单独处理这些问题 - 这超出了问题的范围。
我定义了一个联合结构,它可以在字符串指针或长整数之间切换(这是由于对齐问题)。结构本身如下所示。
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 确定这个数据结构存在内存泄漏。
验证此结构是内存泄漏原因的方法(即我确定这是原因而不是其他原因的原因)是重载当前正在使用的所有运算符(=、<<、 >>) 并且有两个版本的数据结构。第一个是您在上面看到的使用 union 的那个。第二个只是在 TextureID 中有一个字符串和一个 long 2 个单独的字段。
第二个实现(不使用指针的那个)没有内存泄漏。
我知道如果将标签设置为 long.That 这可能会导致分段错误不是问题,问题是不知何故,尽管有明确调用 delete() 分配的内存没有被删除(目前程序中没有任何东西将标签值设置为 long 所以也没有发生段错误)。
编辑:
有人要求我提供内存泄漏的证据,所以这里是:
此版本不会导致内存泄漏:
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;}
这看起来不对:
~TextureID()
{
delete(tag.path);
}
tag.path
仅在设置后有效(即如果设置 tag.id 则无效)。因此,您需要确保仅在设置了 tag.path
时才调用 delete。
~TextureID()
{
if (type == H_STRING) {
delete(tag.path);
}
}
您的第一个代码示例中的问题几乎肯定在
TextureID& operator= (std::string str) {delete(tag.path); tag.path = new std::string(str); type=H_STRING; return *this;}
这假设 tag.path
可以被删除。如果不是这样,它将导致未定义的行为 - 例如,如果 type == H_LONG
.
虽然这是否真的会导致泄漏值得商榷,但未定义行为的症状可以是任何东西,包括来自 valgrind 等工具的内存泄漏的虚假报告。
无论如何,一个简单的解决方法是更改此运算符以在执行 delete tag.path
之前检查 if(type == H_STRING)
。
第二个示例不会导致泄漏,因为 struct
单独包含成员,默认情况下,编译器将确保析构函数正确清理所有成员(调用析构函数等)。
正如 PaulMcKenzie 在评论中指出的那样,第二个示例还有其他问题也可能导致未定义的行为(尽管在实践中可能不是内存泄漏)。我将单独处理这些问题 - 这超出了问题的范围。