4

I'm writing a function to load a txt file and return a const char* the function below works, my question is will this function cause a memory leak if I don't store *pS and then call 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;
}

the function is used in my code as follows

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

Will this delete the string ?

LihO
  • 41,190
  • 11
  • 99
  • 167
lafferc
  • 2,741
  • 3
  • 24
  • 37

4 Answers4

7

"Will this delete the string ?"

No. It will try to delete std::string's underlying character storage yielding an undefined behavior.
Even if it successfully freed that storage, there are other std::string's members that it wouldn't take care of, so yes, apart from undefined behavior, there's a memory leak as well.

Solution: Change your function to return std::string object instead. Alternativelly you might return std::vector<std::string> containing lines, which seems more reasonable than adding "\n".


To avoid memory leaks:
  • avoid dynamic allocation always when it is possible and
  • when it's not possible and you have to use new or new[], then make sure that:
    • for every new there's an appropriate delete call and
    • for every new[] there's an appropriate delete[] call.

    (note that this might be harder than it seems... especially when you're dealing with error-prone code while you still need to take care of every possible return path ~ that's one of the main reasons why it is always preferable to use RAII and smart pointers in C++)

Community
  • 1
  • 1
LihO
  • 41,190
  • 11
  • 99
  • 167
  • @ExpatEgghead: What do you mean with `*sP` ? – LihO Oct 11 '13 at 12:53
  • This will not per se try to delete the `string`'s internal storage, as the pointer `data` is undefined when it is returned. – rubenvb Oct 11 '13 at 12:55
  • @rubenvb: [`std::string::c_str`](http://en.cppreference.com/w/cpp/string/basic_string/c_str) returns *"Pointer to the underlying character storage."* – LihO Oct 11 '13 at 12:58
  • 1
    @rubenvb: I'm not sure what you mean with *"pointer data is undefined when it is returned"*... note that he did `new std::string` – LihO Oct 11 '13 at 13:00
  • Moreover, pointer returned by `c_str()` might not point to dynamically allocated buffer at all. – jrok Oct 11 '13 at 13:06
  • @jrok: Well, that's one of the main reason why `delete str.c_str()` causes *undefined behavior*. – LihO Oct 11 '13 at 13:10
  • @LihO Right, I was a bit too strict on `c_str()`. I reread `` in the n3337 and this usage is fine, if not leaky. – rubenvb Oct 11 '13 at 13:27
1

This function does indeed cause a memory leak, and the use in code you've shown will invoke Undefined Behaviour to boot.

The function causes a leak because you dynamically allocate a std::string (the one you store in pS) and then lose its address once loadFile returns. There's no way to deallocate this string any more, so it's leaked.

This code:

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

is even worse. You're obtaining a pointer which you have not allocated via new (it came from c_str()), and you're calling delete on it. That's undefined behaviour (most likely memory corruption), pure and simple.

The correct thing to do would be to change the function and just return the std::string:

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

If and when the caller needs a const char* out of this, they can call c_str() themselves.

Angew is no longer proud of SO
  • 167,307
  • 17
  • 350
  • 455
1

In order to avoid the memory leak and Undefined Behavior, instead of returning a char*, return a string.

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

This returns a string by value, but under optimization the compiler will often elide the copy.

If you absolutely must return a char*, then do it right:

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

Remember that on the other side of this you will have to delete this pointer, which means using delete []:

int main()
{
  const char* data = loadFile (...);
  delete [] data;
}
John Dibling
  • 99,718
  • 31
  • 186
  • 324
0

Use

return strdup(text.c_str());

The result is deletable. When you return a char* you won't be able to avoid doing your own memory management. Way better way would be to just return an std::string.

dornhege
  • 1,500
  • 8
  • 8