Default Constructor vs. Empty Constructor
While coding away new classes for your C++ project, do you have the habit of just dropping in a contructor definition, just in case you may in the future need to do some initialization at construction? A while ago, I had wondered if there would be any difference at compile time between having an empty constructor and having no constructor at all (and thus using the default constructor). I did a quick investigation by writing a simple test application and looking at the disassembly. It turned out that there is actually a difference (in a debug build).
I defined a class called “MyClass” which has a single integer member variable (of no significance to this test). The main function defines an array of 42 MyClass objects and then does some dummy arithmetic, just as a landmark for the assembly code following the initialization (if any) of the MyClass array.
I’ve interlaced the C++ code with the corresponding assembly code (teal) along with some comments (gray) so that you can see what’s happening behind the scenes for each line of C++ code. I believe the compiler I used was MS VC.
First, let’s see what happens when using the default constructor (not defining our own):
class MyClass
{
private:
int m_i;
};
int main(int argc, char* argv[])
{
00401000 push ebp
00401001 mov ebp,esp
Allocate space in stack for our local variables;
42 (array length) * 4 (size of MyClass) = 168
2 (local variables x and y) * 4 (size of int) = 8
–
176 = 0xB0 (total)
00401003 sub esp,0B0h
MyClass a[42];
int x = 2;
Assign 2 to x
00401009 mov dword ptr [x],2
int y = x * 2;
Get x
00401010 mov eax,dword ptr [x]
Multiply by 2
00401013 shl eax,1
Assign to y
00401015 mov dword ptr [y],eax
return 0;
Reset return value to zero
00401018 xor eax,eax
}
You can see that there’s absolutely no compiled code associated with our array definition, apart from the single line of code for making room in the stack.
Let’s compare that to what happens when using an empty constructor:
class MyClass
{
public:
MyClass()
{
}
private:
int m_i;
};
int main(int argc, char* argv[])
{
00401000 push ebp
00401001 mov ebp,esp
Allocate space in stack for our local variables;
42 (array length) * 4 (size of MyClass) = 168
2 (local variables x and y) * 4 (size of int) = 8
3 (internal loop variables) * 4 (size of int) = 12
–
188 = 0xBC (total)
00401003 sub esp,0BCh
MyClass a[42];
Loop 42 (0×2A) times
00401009 mov dword ptr [ebp-0B4h],2Ah
00401013 mov dword ptr [ebp-0B8h],4
0040101D lea eax,[a]
00401023 mov dword ptr [ebp-0BCh],eax
Execute the following code in the loop:
00401029 mov ecx,dword ptr [ebp-0B4h]
0040102F sub ecx,1
00401032 mov dword ptr [ebp-0B4h],ecx
00401038 cmp dword ptr [ebp-0B4h],0
0040103F jl main+55h (401055h)
00401041 mov edx,dword ptr [ebp-0BCh]
00401047 add edx,dword ptr [ebp-0B8h]
0040104D mov dword ptr [ebp-0BCh],edx
00401053 jmp main+29h (401029h)
int x = 2;
00401055 mov dword ptr [x],2
int y = x * 2;
0040105C mov eax,dword ptr [x]
0040105F shl eax,1
00401061 mov dword ptr [y],eax
return 0;
00401064 xor eax,eax
}
Whoops! There’s suddenly a big chunk of code (magenta) introduced. If you look at what that piece of code is doing, it’s looping 42 times, but not doing anything apart from the overhead of the loop itself. This could potentially become significant if our array had hundreds of thousands of items instead of just a mere 42. Of course, this was what I observed with a MS VC debug build and other compilers may have different results, with different compiler optimization settings.