【问题标题】:Memory leak/management advice (new array in method) (c++)内存泄漏/管理建议(方法中的新数组)(c++)
【发布时间】:2015-11-01 20:10:51
【问题描述】:

我正在关注 youtube 上的教程,并尝试在文件输入法中稍微更改代码:

static std::string read_file(const char* filepath){
    FILE* file = fopen(filepath, "rt");     //read text file
    fseek(file, 0, SEEK_END);
    unsigned long length = ftell(file);
    char* data = new char[length + 1];
    memset(data, 0, length + 1);
    fseek(file, 0, SEEK_SET);
    fread(data, 1, length, file);
    fclose(file);

    std::string result(data);
    delete[] data;
    return result;
}

这是原始代码,将在此上下文中使用:

std::string vertSourceString = read_file(m_VertPath);
const char *source = vertSourceString.c_str();

所以我想“我为什么要使用 String?我必须包含它,然后我必须将所有内容复制到字符串,以便我可以再次将其复制到 char*?所以我尝试了这个:

static char* read_file(const char* filepath){
    FILE* file = fopen(filepath, "rt");     //read text file
    fseek(file, 0, SEEK_END);
    unsigned long length = ftell(file);
    char* data = new char[length + 1];
    memset(data, 0, length + 1);
    fseek(file, 0, SEEK_SET);
    fread(data, 1, length, file);
    fclose(file);

    return data;
}

但是如果我不删除它,它有一个非常明显的内存泄漏:

char *c = read_file("src/shaders/basic.vert");
delete c;

那么,有没有办法在方法中删除它,仍然返回它的副本? 需要删除的方法似乎不是很好的做法。 我应该回到以前的实现吗?

PS:我知道 C++ 有一种“更简单”的读取文件的方式 (fstream),但根据一项小型研究(我承认,没有任何测试在我身边)这种方式通常更快

【问题讨论】:

  • 如果你的想法是“我为什么要使用char *?我应该在任何地方都使用string。”
  • 抱歉,应该说明我正在将它与 OpenGL 一起使用来读取着色器并用于:“void glShaderSource(GLuint shader, GLsizei count, const GLchar **string, const GLint *length); "所以我想我迟早会需要 char *

标签: c++ memory-management memory-leaks


【解决方案1】:

您可以像这样使用std::stringstd::ifstreamstd::ostringstream 重写它:

#include <string>
#include <sstream>
#include <fstream>

static std::string read_file(const std::string& filepath)
{
    std::ostringstream oss;
    oss << std::ifstream(filepath, std::ios::binary).rdbuf();
    return oss.str();
}

另外,通过去掉“中间人”(std::ostringstream),这种方法可能会更有效(但不太优雅):

static std::string read_file2(const std::string& filepath)
{
    std::ifstream ifs(filepath, std::ios::binary|std::ios::ate);

    std::string buf;
    buf.resize(ifs.tellg());
    ifs.seekg(0);

    if(ifs.read(&buf[0], buf.size()))
        return buf;

    return {};
}

注意:这里的第二种方法是我使用 std::ostringstreamstd::FILE* fp = fopen() 和 posix int fd = open() 测试的 4 种不同方法中最快的方法。

【讨论】:

  • 这不会落到使用字符串的原始问题上吗?这意味着创建字符串,然后返回该字符串的副本,然后将复制的字符串复制到 char 数组中?
  • @Sinn 不,字符串根本不应该被复制。在最坏的情况下,字符串应该在返回语句中移动,但更有可能整个操作将被编译器省略并临时返回oss.str() 将在调用者的堆栈上就地构造..
【解决方案2】:

动态内存分配,由 C++ 中的程序员管理。 然后,问题是,除非您事先知道文件的大小,然后在方法外部分配一个缓冲区,然后传递它,否则您不能这样做。 比如:

dosomething(file, file_size) {
char a[file_size]; //a is a pointer.
read_file(file, a);
}

然后在read_file中:

/* Where a is char * parameter */
memset(a, 0, length + 1);
fseek(file, 0, SEEK_SET);
fread(a, 1, length, file);

通过执行这样的操作,您不必为 char 内容手动释放内存,因为它的堆栈内存,并且在退出范围时它会自动释放。

同样,这种方法需要您事先知道文件大小。

【讨论】:

  • 顺便说一句,如果您知道文件大小永远不会大于某个值,您可以分配一个巨大的缓冲区。
  • 感谢您提供的替代方案,但似乎我最好的选择是使用字符串并在需要“char *”时使用“c_ctr()”。至少这样我就不必担心提前获得大小或在之后删除它
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-12-28
  • 1970-01-01
  • 1970-01-01
  • 2014-04-08
  • 1970-01-01
  • 1970-01-01
  • 2014-05-01
相关资源
最近更新 更多