Tip 9: Optimizing Class Member Alignment
The size of a class can be changed simply by playing with the order of its members'
declaration:
struct A
{
bool a;
int b;
bool c;
}; /*sizeof (A) == 12*/
On my machine, sizeof (A) equals 12. This result might seem surprising because the
total size of A's members is only 6 bytes: 1+4+1 bytes. Where did the remaining
6 bytes come from? The compiler inserted 3 padding bytes after each bool member
to make it align on a four-byte boundary. You can reduce A's size by reorganizing
its data members as follows:
struct B
{
bool a;
bool c;
int b;
}; // sizeof (B) == 8
This time, the compiler inserted only 2 padding bytes after the member c. Because
b occupies four bytes, it naturally aligns on a word boundary without necessitating
additional padding bytes.
Tip 10: Differences between Postfix and Prefix Operators
The built-in ++ and operators can appear on both sides of their operand:
int n=0;
++n; /*prefix*/
n++; /*postfix*/
You probably know that a prefix operator first changes its operand before taking
its value. For example:
int n=0, m=0;
n = ++m; /*first increment m, then assign its value to n*/
cout << n << m; /* display 1 1*/
In this example, n equals 1 after the assignment because the increment operation
took place before m's value was taken and assigned to n. By contrast,
int n=0, m=0;
n = m++; /*first assign m's value to n, then increment m*/
cout << n << m; /*display 0 1*/
In this example, n equals 0 after the assignment because the increment operation
took place after m's original value was taken and assigned to n.
To understand the difference between postfix and prefix operators better, examine
the disassembly code generated for these operations. Even if you're not familiar
with assembly languages, you can immediately see the difference between the two;
simply notice where the inc (increment) assembly directive appears:
/*disassembly of the expression: m=n++;*/
mov ecx, [ebp-0x04] /*store n's value in ecx register*/
mov [ebp-0x08], ecx /*assign value in ecx to m*/
inc dword ptr [ebp-0x04] /*increment n*/
/*disassembly of the expression: m=++n;*/
inc dword ptr [ebp-0x04] /*increment n;*/
mov eax, [ebp-0x04] /*store n's value in eax register*/
mov [ebp-0x08], eax /*assign value in eax to m*/
Tip 11: Eliminating Temporary Objects
C++ creates temporary objects "behind your back" in several contexts.
The overhead of a temporary can be significant because both its constructor and
destructor are invoked. You can prevent the creation of a temporary object in most
cases, though. In the following example, a temporary is created:
Complex x, y, z;
x=y+z; /* temporary created */
The expression y+z; results in a temporary object of type Complex that stores the
result of the addition. The temporary is then assigned to x and destroyed subsequently.
The generation of the temporary object can be avoided in two ways:
Complex y,z;
Complex x=y+z; /* initialization instead of assignment */
In the example above, the result of adding x and z is constructed directly into
the object x, thereby eliminating the intermediary temporary. Alternatively, you
can use += instead of + to get the same effect:
/* instead of x = y+z; */
x=y;
x+=z;
Although the += version is less elegant, it costs only two member function calls:
assignment operator and operator +=. In contrast, the use of + results in three
member function calls: a constructor call for the temporary, a copy constructor
call for x, and a destructor call for the temporary.