Reputation: 37832
It's been many, many years since the last time I did something in C++. These days, someone asked me for some help on a school project in C++, and I was intrigued by a "feature" of the language I've seen, which worked fine but I expected it not to work.
As I remember, I can have instances of classes created either on the heap or on the stack:
int main() {
MyClass *inHeap = new MyClass();
MyClass inStack = MyClass();
}
As far as I can tell, the first variable, inHeap
, will make the compiler reserve some stack in the main
stack frame enough to hold a pointer (4 bytes? 8 bytes? something like that), which points to the actual instance of the object which lives in the heap.
Also, the second variable, inStack
, will make the compiler reserve stack enough to hold the complete instance of MyClass
right there in the main
stack frame.
Now, on to my question. Suppose I have a function that is supposed to return an instance of MyClass
. At first, I thought it could only return instances in the heap:
MyClass *createInHeap() {
return new MyClass();
}
int main() {
MyClass* inHeap = createInHeap();
}
But what I've seen is the following:
MyClass createInStack() {
MyClass c = MyClass();
return c;
}
int main() {
MyClass inStack = createInStack();
}
What exactly is going on here?
Is the memory for the instance of MyClass
reserved in the stack frame of createInStack
? If this is the case, will this code force the instance to be copied to the stack frame of main
when the function createInStack
returns? How is this copy performed, ie, is it just automatically calling the copy constructor for me in the main
function?
Another possibility I thought about was that the compiler would be smart enough to already reserve the memory for the MyClass
instance in the main
stack frame. Does this exist? Is this some kind of optimization to avoid making a possibly expensive copy?
As a final question, when I have an instance created in the stack, when exactly is its destructor called? When the scope in which it was created finishes?
Upvotes: 3
Views: 2005
Reputation: 64308
If you are doing something like this:
MyClass createInStack()
{
MyClass return_value;
return return_value;
}
int main()
{
MyClass inStack = createInStack();
}
Then yes, c
is logically created on the stack in createInStack()
and then a copy of it is logically returned and then copied into inStack
in main
.
There is a common misconception that returning by value is inefficient because of all this logical copying. However, due to named return value optimization, this isn't what actually happens in practice. The construction step in the calling function is just deferred to the called function. It would be something like this (pseudocode):
void createInStack(MyClass &return_value)
{
return_value.MyClass(); // construct return_value (not actually valid syntax)
}
int main()
{
MyClass inStack; // except don't call the constructor
createInStack(inStack);
}
So as you can see, no actual copying happens.
In addition, the compiler may do other optimizations. It may even decide that inStack
is never used and just not create it, but you can be pretty sure that at least the named return value optimization will avoid a lot of copying.
Upvotes: 9
Reputation: 129454
It should also be pointed out that calling new
is not a cost-less operation. It can have significant overhead, and it certainly uses more memory than making a local copy on the stack - and of course, the delete
has to be handled and remembered by the programmer, and will also take more than zero time.
So it's, as usual, a design decision which is better, and understanding what is going on (as well as understanding the "Return Value Optimization" already covered in a previous answer). A very large class will take a long time to copy, but a small class with just a few values in it, may well be faster to copy on the stack than allocating and freeing it.
Upvotes: 1