STL 容器和内存泄漏

STL containers & memory leaks

本文关键字:内存 泄漏 STL      更新时间:2023-10-16

C#编码器刚刚编写了一个简单的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) 更普遍地说,按值返回对象的容器是否会泄漏内存?(假设对象本身没有泄漏)

while (theFile.good()) {
 getline (theFile, line);
    lines.push_back(line);
}

忘记效率吧,这段代码是不正确的。它将无法正确读取文件。请参阅以下主题了解为什么

  • 什么';从C++文件中读取行的首选模式是什么

所以循环应该写成:

while (getline (theFile, line)) {
    lines.push_back(line);
}

现在这是正确的。如果您想提高效率,请先对您的应用程序进行配置文件。尝试查看占用大部分CPU周期的部分。


(1) 这个代码会泄露内存吗?

没有。

(2) 更普遍地说,按值返回对象的容器是否会泄漏内存?

取决于容器中对象的类型。在您的情况下,std::vector中对象的类型std::string,这可以确保不会泄漏内存。

No和No。按值返回永远不会泄露内存(假设容器和包含的对象写得很好)。如果是其他方式,那将是相当无用的。

我支持纳瓦兹所说的,你的while循环是错误的。坦率地说,令人难以置信的是,我们看到了多少次,肯定有很多糟糕的建议。

(1) 这个代码会泄露内存吗?

(2) 更普遍地说,按值返回对象的容器是否会泄漏内存?

没有。您可能会通过指针或通过泄漏的对象泄漏存储在容器中的内存。但这并不是因为按值返回。

我知道这个代码并不有效;文本行在读取时复制一次,在按值返回时复制第二次。

很可能不会。字符串有两个副本,但不是您正在考虑的副本。返回副本很可能在C++03中进行优化,并且在C++11中要么被优化掉,要么被转换为移动(廉价)。

这两个范围是:

getline (theFile, line);
lines.push_back(line);

第一行从文件复制到line,第二行从line复制到容器。如果您使用的是C++11编译器,您可以将第二行更改为:

lines.push_back(std::move(line));

将字符串的内容移动到容器中。或者(在C++03中也是有效的),您可以用更改这两行

lines.push_back(std::string()); // In most implementations this is *cheap*
                                // (i.e. no memory allocation)
getline(theFile, lines.back());

您应该测试读取的结果(如果读取失败,在最后一种选择中,确保resize少一个元素以删除最后一个空字符串。

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

否,按值返回容器不应泄漏内存。标准库的设计在任何情况下都不会泄漏内存。只有当它的实现中存在错误时,它才能泄漏内存。至少在一个旧的MSVC中,字符串向量曾经有一个错误。