So, I've always been a little fuzzy on C++ pointers vs. ... whatever the other one is called. Like,
Object* pointer = new Object();
vs.
Object notpointer();
I know that pointers are probably involved in the second one, but basically it's a not-pointer. (What's it actually called?)
Furthermore, I believe that for the first one, you have to call
delete pointer;
at some point once you're done with it, right? And the other one you don't need to worry about. I've read that the first is allocated on the heap, but the second is allocated on the stack, and goes away when the method returns.
However, what about when you're returning something (not a primitive) from a function?
A good example is written in Should I return std::strings?:
std::string linux_settings_provider::get_home_folder() {
return std::string(getenv("HOME"));
}
By what I previously wrote, the string is allocated on the stack, and should be freed when the function returns, right? Yet nobody said anything about that, so I assume it works fine. Why?
In general, what's the difference between
return new std::string("pointer");
and
return std::string("not-pointer");
?
Also, assuming both work, what are the pros and cons of the two?
When you return by pointer, you need to return a dynamically allocated object the way that you show (i.e. returning a pointer to a stack object results in undefined behavior if it is dereferenced later). This creates a potential for memory leaks, because, like you have noted, that object needs to be deleted explicitly.
Returning by value, on the other hand (i.e. the second snippet) results in copying the object that you return from the stack object into the object that receives the return value:
std::string res = get_home_folder(); // std::string gets copied into res
Compilers can optimize this to avoid copying through return value optimization.
I guess you didn't really want to write Object notpointer();
as this actually declares a function called notpointer
returning an Object
. If you meant Object notpointer;
the entities in question are call values. Values are, indeed, allocated on the stack or, when the happen to be members of objects, embedded into the object.
When returning anything, the entity being returned is copied or moved into the location where the actual return value is expected. Once the return value is constructed, the local object, i.e., those on the stack, go out of scope and are destroyed. Given that the local objects are going away anyway, the compiler is allowed to elide the copy and construct the object immediately in the correct location, even if the copy constructor and/or the destructor have side effects.
The difference between your two return statements is
When using new std::string("pointer")
you get an object allocated on the heap and you return a pointer to the object. It is very easy to leak that pointer and you'd be better off to put it immediately into a suitable object, e.g., a std::unique_ptr<std::string>
:
return std::unique_ptr<std::string>(new std::string("pointer"));
This way, the pointer won't be leaked. Of course, you'd also change the return type to be a std::unique_ptr<std::string>
instead a std::string*
. Note, that heap allocations are generally fairly expensive.
std::string("value")
you just return a local variable. There is a good chance that the copy will be elided and the return value is constructed immediately in the location where it should go. There is no need to look after any resources as all objects involved will be destroyed automatically. There is no explicit heap allocation and stack allocation are very fast.Of course, in the given example the std::string
actually needs to allocate its internal representation in both cases. If the pointer is returned, it is guaranteed that there is no additional internal allocation. On the other hand, when return a std::string
value and it actually needs to be copied indeed, the internal memory may need to be allocated for the copy. That is, when returning large objects, possibly requiring lots of memory allocations, the approach returning a value runs the risk that it needs to be copied. With the current C++ the objects are, however, moved in the worst case, i.e., there isn't as much of a concern about copying the objects as there was in C++03.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With