Reputation:
I would like some clarification on what happens when a class is instantiated on the stack.
When a C++ class is instantiated on the heap:
MyClass *myclass = new MyClass();
a pointer is created of type MyClass and the class is instantiated on the same line by "new MyClass();". Stretching it out like this:
MyClass *myclass;
myclass = new MyClass();
If I'm not mistaken, a pointer is created on the 1st line and then memory is allocated for the instance on the 2nd line and a pointer to the address of the instance is assigned to myclass.
Does this mean that when a class is instantiated on the stack this way:
MyClass myclass = MyClass();
that it's created twice?
Upvotes: 5
Views: 18467
Reputation: 299810
As far as the Standard is concerned there is no notion of stack and heap. However, all C++ implementation that I know of will map the concepts "automatic storage duration" and "dynamic storage" into stack (*) and heap respectively.
(*) As remarked by @MooingDuck
, this is only true for function variables. Globals and static variables (probably) have automatic storage duration, yet they are not on the stack.
Now this is cleared:
new
are stored in the heap (and their address returned)With examples, to be a bit more visual:
void f0() {
Class* c = new Class();
}
void f1() {
Class* c = 0;
c = new Class();
}
Here c
(of type Class*
) is stored on the stack and points to an object (of type Class
) that is stored on the heap
void f2() {
Class c = Class();
}
void f3() {
Class c;
}
Here c
is stored on the stack. In f2
there might be a temporary (object without name) created by the expression Class()
and then copied into c
(depending on whether the compiler elides the copy or not), the storage of temporaries is not addressed by the Standard... they commonly use the stack though.
A final word: whether this ends up actually using some space on the stack or not is another matter.
In action:
// Simple test.cpp
#include <cstdio>
struct Class { void foo(int& a) { a += 1; } };
int main() {
Class c;
int a = 0;
c.foo(a);
printf("%d", a);
}
The compiler (using Clang/LLVM... slightly reworked) generates:
@.str = private unnamed_addr constant [3 x i8] c"%d\00", align 1
define i32 @main() nounwind uwtable {
%1 = tail call i32 (i8*, ...)* @printf(@.str, i32 1)
ret i32 0
}
Note how: 1. The class has been removed, 2. The call to foo
has been removed, 3. a
does not even appear. Translated back to C++ we get:
#include <cstdio>
int main() {
printf("%d", 1);
}
And if we generate the assembly (64-bit X86):
main: # @main
pushq %rax # save content of 'rax' on the stack
movl $.L.str, %edi # move address of "%d" into the 'edi' register
movl $1, %esi # move 1 into the 'esi' register
xorb %al, %al # --
callq printf # call printf, it'll look up its parameters in registers
xorl %eax, %eax # --
popq %rdx # restore content from stack to 'rdx'
ret # return
Note how constants ($1
and $.L.str
) are pushed into registers (%esi
and %esi
resp.) and never "hit" the stack. The only stack manipulations are pushq
and popq
(and I have no idea what they actually save/restore.
Upvotes: 6
Reputation: 12963
My post is just to complete Luchian Grigore’s and address a nabulke’s question
The fact that MyClass myclass = MyClass();
does not call the assignment operator is stated in the C++96 norm, in the paragraph 12.6.1.1 ( http://www.csci.csusb.edu/dick/c++std/cd2/special.html ). The line says : “a single assignment-expression can be specified as an initializer using the = form of initialization.”
I hope this helps
Upvotes: 0
Reputation: 53047
"Stack" and "heap" have no definition in C++, memory is not required to be allocated anywhere.
With you example here:
MyClass myclass = MyClass();
You are initializing myclass
via copy constructor to the temporary object. myclass
(and the temporary) has automatic storage which you can consider being allocated on "the stack" if that makes you happy.
Copy elision is allowed in this case, essentially optimizing it to MyClass myClass
, however note that it cannot always do this, such as when the copy constructor is private.
Here is an example that you can test:
struct obj {
static int c;
int myc;
obj() : myc(c++) {
std::cout << "ctor of " << myc << '\n';
}
obj(const obj&) : myc(c++){
std::cout << "copy ctor of " << myc << '\n';
}
~obj() {
std::cout << "dtor of " << myc << '\n';
}
};
int obj::c = 1;
int main(int argc, char** argv)
{
obj x = obj();
}
If the copy is elided you will see:
ctor of 1
dtor of 1
Otherwise (gcc option -fno-elide-constructors to prevent the elision happening):
ctor of 1
copy ctor of 2
dtor of 1
dtor of 2
Additionally, making the copy constructor private will give a compiler error.
Upvotes: 7
Reputation: 31435
In the case of using the pointer, the first line just declares a pointer as you say. the line with new does more than just allocate memory, it will also invoke the default constructor of MyClass to be called. You can do that in one line through:
MyClass * myClass = new MyClass;
The parentheses after MyClass are optional when it is constructed with no parameters.
The object would not be created on the stack and will exist until delete is called on the pointer. If that does not ever happen you will have a leak.
In the case of
MyClass myClass = MyClass();
The class would be created twice, firstly with the default constructor and then with the copy-constructor. The compiler might optimise it away to a single construction, but you should really just initialise:
MyClass myClass;
Note here that you must not use parentheses in the declaration or it will declare a function and not an instance of the class.
In this case one instance will be created on the stack. Your method may create a "temporary" which is a sort-of stack variable, on the way to being copy-constructed. You can think of the temporary as being "returned" by the constructor and return values, which is a tricky sort of area, are in general automatic and use stack space.
Upvotes: 6