STL 容器和内存泄漏
Posted
技术标签:
【中文标题】STL 容器和内存泄漏【英文标题】:STL containers & memory leaks 【发布时间】:2012-08-30 19:56:02 【问题描述】:C# coder 刚刚编写了这个简单的 C++ 方法来从文件中获取文本:
static std::vector<std::string> readTextFile(const std::string &filePath)
std::string line;
std::vector<std::string> lines;
std::ifstream theFile(filePath.c_str());
while (theFile.good())
getline (theFile, line);
lines.push_back(line);
theFile.close();
return lines;
我知道这段代码效率不高;文本行在读取时复制一次,在按值返回时复制第二次。
两个问题:
(1) 这段代码会泄漏内存吗? (2) 更一般地说,按值返回对象容器是否会泄漏内存? (假设对象本身不泄漏)
【问题讨论】:
你可能不希望static
像在 C# 中那样无处不在。此外,容器会在它们被销毁时释放内存,当它们像你一样在堆栈上分配时,就是它们超出范围时。
实际上按值返回并不一定会导致副本。由于RVO,它可以避免,或者,如果使用 c++11 并且 RVO 是不可能的,则返回值被移动而不是被复制(移动 vector
大约需要三个指针分配,所以这并不是真的花费很多)。不,如果对象本身是正确的,按值返回对象不会泄漏内存。在 c++ 中,除非您开始使用 new
更好:while (std::getline(theFile, line))
,否则你会在 eof 处得到一个额外的空行。除此之外,代码很好。在 C++11 中,您有惯用的方法来避免额外的副本。在 C++03 中,您通常更喜欢额外的副本而不是卷积代码。
【参考方案1】:
while (theFile.good())
getline (theFile, line);
lines.push_back(line);
忘了效率吧,这段代码是不正确的。它不会正确读取文件。请参阅以下主题了解原因:
What's preferred pattern for reading lines from a file in C++?所以循环应该写成:
while (getline (theFile, line))
lines.push_back(line);
现在这是正确的。如果您想提高效率,请先分析您的应用程序。尝试查看占用 CPU 周期最多的部分。
(1) 这段代码会泄漏内存吗?
没有。
(2) 更一般地说,按值返回对象容器是否会泄漏内存?
取决于容器中对象的类型。在您的情况下,std::vector
中对象的 type 是 std::string
,这确保不会泄漏任何内存。
【讨论】:
【参考方案2】:没有,也没有。按值返回永远不会泄漏内存(假设容器和包含的对象写得很好)。如果换成其他方式,那就太没用了。
我赞同 Nawaz 所说的,你的 while 循环是错误的。坦率地说,令人难以置信的是,我们看到了多少次,肯定有很多糟糕的建议。
【讨论】:
谢谢 - 我在“www.cplusplus.com”得到了循环结构。我想即使是这样的名字,也必须小心剪裁 sn-ps。 @tpascale 想知道确切的地址。 cplusplus.com/doc/tutorial/files 并查看“文本文件”部分中的第二个示例。 有趣,文字其实是OK的,它暗示good()是假的在最后一次I/O操作失败之后。但如果作者知道他们为什么要这样写循环呢?【参考方案3】:(1) 这段代码会泄漏内存吗?
没有
(2) 一般情况下,按值返回对象容器是否会泄漏内存?
没有。您可能会泄漏通过指针或泄漏对象存储在容器中的内存。但这不会是由按值返回造成的。
我知道这段代码效率不高;文本行在读取时复制一次,在按值返回时复制第二次。
很可能不是。该字符串有两个副本,但不是您正在考虑的那些。返回的副本很可能会在 C++03 中进行优化,并且在 C++11 中将被优化掉或转换为移动(廉价)。
这两个应对措施相当:
getline (theFile, line);
lines.push_back(line);
第一行从文件复制到line
,第二行从line
复制到容器。如果您使用的是 C++11 编译器,则可以将第二行更改为:
lines.push_back(std::move(line));
to move 将字符串的内容移动到容器中。或者(在 C++03 中也有效),您可以更改这两行:
lines.push_back(std::string()); // In most implementations this is *cheap*
// (i.e. no memory allocation)
getline(theFile, lines.back());
并且你应该测试读取的结果(如果读取失败,在最后一个替代方案中,确保resize
减少一个元素以删除最后一个空字符串。
【讨论】:
要删除最后一个元素,可以使用pop_back
。这比得到长度要简单得多,减去 1 和 resize
ing。【参考方案4】:
在 C++11 中,你可以这样做:
std::vector<std::string>
read_text_file(const std::string& path)
std::string line;
std::vector<std::string> ans;
std::ifstream file(path.c_str());
while (std::getline(file, line))
ans.push_back(std::move(line));
return ans;
并且不会制作额外的副本。
在 C++03 中,您接受额外的副本,并且只有在分析要求时才痛苦地删除它们。
注意:您不需要手动关闭文件,std::ifstream
的析构函数会为您完成。
注意2:您可以在 char 类型上进行模板化,这在某些情况下可能很有用:
template <typename C, typename T>
std::vector<std::basic_string<C, T>>
read_text_file(const char* path)
std::basic_string<C, T> line;
std::vector<std::basic_string<C, T>> ans;
std::basic_ifstream<C, T> file(path);
// Rest as above
【讨论】:
【参考方案5】:不,按值返回容器不应泄漏内存。标准库被设计为在任何情况下都不会泄漏内存。如果其实现中存在错误,它只能泄漏内存。至少在旧的 MSVC 中曾经有一个 bug in vector of strings。
【讨论】:
以上是关于STL 容器和内存泄漏的主要内容,如果未能解决你的问题,请参考以下文章