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.

Leave a Reply