Passing & returning by value
To understand the need for the
copy-constructor, consider the way C handles passing and returning variables by
value
during
function calls. If you declare a function and make a function
call,
int f(int x, char c);
int g = f(a, b);
how does the compiler know how to pass
and return those variables? It just knows! The range of the types it must deal
with is so small – char, int, float, double,
and their variations – that this information is built into the compiler.
If you figure out how to generate
assembly
code with your compiler and determine the statements generated by the function
call to f( ), you’ll get the equivalent of:
push b
push a
call f()
add sp,4
mov g, register a
This code has been cleaned up
significantly to make it generic; the expressions for b and a will
be different depending on whether the variables are global (in which case they
will be _b and _a) or local (the compiler will index them off the
stack pointer). This is also true for the expression for g. The
appearance of the call to f( ) will depend on your name-decoration
scheme, and “register a” depends on how the CPU registers are named
within your assembler. The logic behind the code, however, will remain the
same.
In C and C++, arguments are first pushed
on the stack from right to left, then the function call is made. The calling
code is responsible for cleaning the arguments off the stack (which accounts for
the add sp,4). But notice that to pass the arguments by value, the
compiler simply pushes copies on the stack – it knows how big they are and
that pushing those arguments makes accurate copies of them.
The return value of f( ) is
placed in a register. Again, the compiler knows everything there is to know
about the return value type because that type is built into the language, so the
compiler can return it by placing it in a register. With the primitive data
types in C, the simple act of copying the bits of the value is equivalent to
copying the object.