【问题标题】:Does deleting dynamically allocated std::string using a pointer returned by c_str() cause a memory leak in C++?使用 c_str() 返回的指针删除动态分配的 std::string 是否会导致 C++ 中的内存泄漏?
【发布时间】:2013-10-19 13:21:05
【问题描述】:

我正在编写一个函数来加载一个 txt 文件并返回一个 const char* 下面的函数有效,我的问题是如果我不存储 *pS 然后调用 delete pS 这个函数会导致内存泄漏吗?

const char* loadFile(string fname)
{
   string line,text;
   ifstream in(fname);
   while(std::getline(in, line))
   {
       text += line + "\n";
   }

   string *pS = new string(text);
   const char* data = pS->c_str();

   return data;
}

该函数在我的代码中使用如下

static const char* pVS;
...
pVS = loadFile("VS.txt");
...
delete pVS;

这会删除字符串吗?

【问题讨论】:

  • std::string NOT 只是一个 char* 缓冲区的包装器。

标签: c++ string memory-management memory-leaks return-value


【解决方案1】:

“这会删除字符串吗?”

否。它将尝试删除std::string 的底层字符存储,从而产生未定义的行为
即使它成功释放了该存储空间,还有其他std::string 的成员不会处理,所以是的,除了未定义的行为,还有一个内存泄漏 em> 也是。

解决方案:将您的函数更改为返回 std::string 对象。或者,您可以返回包含行的 std::vector<std::string>,这似乎比添加 "\n" 更合理。


为避免内存泄漏:
  • 尽可能避免动态分配,并且
  • 当它不可能并且您必须使用newnew[] 时,请确保:
    • 对于每个new,都有一个适当的delete 调用和
    • 对于每个new[],都有一个适当的delete[] 调用。

    (请注意,这可能比看起来更难......尤其是当你处理容易出错的代码时,你仍然需要处理每一个可能的返回路径~这就是为什么它总是最好使用RAII and smart pointers in C++)

【讨论】:

  • @ExpatEgghead:*sP 是什么意思?
  • 这本身不会尝试删除string的内部存储,因为指针data在返回时是未定义的。
  • @rubenvb:std::string::c_str 返回“指向底层字符存储的指针。”
  • @rubenvb:我不确定你的意思是什么“指针数据在返回时未定义”...注意他做了new std::string
  • 此外,c_str() 返回的指针可能根本不指向动态分配的缓冲区。
【解决方案2】:

使用

return strdup(text.c_str());

结果是可删除的。当您返回一个 char* 时,您将无法避免自己进行内存管理。更好的方法是只返回一个std::string

【讨论】:

  • 结果不可删除。它是免费的。
  • strdup 既不在标准 C 中也不在标准 C++ 中。这是一个 POSIX 的事情。
【解决方案3】:

这个函数确实会导致内存泄漏,在你展示的代码中使用会调用未定义的行为来启动。

该函数导致泄漏,因为您动态分配了一个std::string(您存储在pS 中的那个),然后在loadFile 返回时丢失了它的地址。没有办法再释放这个字符串,所以它被泄露了。

这段代码:

pVS = loadFile("VS.txt");
...
delete pVS;

更糟。您正在获得一个尚未通过new 分配的指针(它来自c_str()),并且您正在调用delete。这是未定义的行为(很可能是内存损坏),纯粹而简单。

正确的做法是更改函数并返回std::string

string loadFile(string fname)
{
   string line,text;
   ifstream in(fname);
   while(std::getline(in, line))
   {
       text += line + "\n";
   }
   return text;
}

如果调用者需要一个const char*,他们可以自己调用c_str()

【讨论】:

    【解决方案4】:

    为了避免内存泄漏和未定义行为,不要返回char*,而是返回string

    std::string loadFile(string fname)
    {
      // ...
       string retval (text);
       // ...
       return retval;
    }
    

    这会按值返回string,但在优化下编译器通常会忽略副本。

    如果你绝对必须返回char*,那么就做对了:

    const char* loadFile(string fname)
    {
       string line,text;
       ifstream in(fname);
       while(std::getline(in, line))
       {
           text += line + "\n";
       }
    
       string retval(text);
       char* data = new char [retval.length()+1];
       strcpy (retval.c_str(), data);
       return data;
    }
    

    请记住,在另一方面,您必须delete 这个指针,这意味着使用delete []

    int main()
    {
      const char* data = loadFile (...);
      delete [] data;
    }
    

    【讨论】:

    • 不要忘记,在第二种情况下,正确的清理应该是delete[],而不是delete
    • @Agnew:是的,我把所有客户端的东西都放在了外面,但我会把它添加进去。
    猜你喜欢
    • 1970-01-01
    • 2021-06-14
    • 1970-01-01
    • 1970-01-01
    • 2012-12-23
    • 2012-08-10
    • 2013-04-14
    • 1970-01-01
    • 2021-05-02
    相关资源
    最近更新 更多