Tuesday , 24 November 2020
Breaking News

Copy Constructors in C++ with Examples


Notice: Undefined index: tie_hide_meta in /home1/adnanonl/public_html/wp-content/themes/sahifa/framework/parts/meta-post.php on line 3

Notice: Trying to access array offset on value of type null in /home1/adnanonl/public_html/wp-content/themes/sahifa/framework/parts/meta-post.php on line 3
Spread the love

Copy Constructors in C++ with Examples

When you understand the basics of C ++ references, you are prepared to deal with one of the most confusing concepts in the language: the copy constructor, often called X (X &) (“X of reference X”). This constructor is essential to control the step and return by value of the types defined by the user in function calls. In fact it is so important that the compiler automatically creates a copy constructor in case the programmer does not provide it.

11.3.1. Step and return by value

To understand the need for the copy constructor, consider how C handles the step and return by value of variables when a function is called. If you declare a function and invoke it,

int f (int x, char c);
int g = f (a, b);
How does the compiler know how to pass and return those variables? He just knows! The range of types you must deal with is so small (char, int, float, double, and its variations), that such information is already inside the compiler.

If you find out how to make your compiler generate assembler code and determine which instructions are used for the invocation of the function f (), you will get something equivalent to:

push b
push to
call f ()
add sp, 4
mov g, register a
This code has been simplified to make it generic; the expressions b and a will be different depending on whether the variables are global (in which case they would be _b and _a) or local (the compiler would put them in the stack). This is also true for g. The syntax of the call to f () would depend on your style guide, and register a would depend on how your assembler calls the CPU registers. Despite the simplification, the logic of the code would be the same.

In both C and C ++, the arguments are first put in the stack from right to left, and then the function is called. The call code is responsible for collecting the arguments of the stack (which explains the statement add sp, 4). But keep in mind that when you pass arguments by value, the compiler simply puts copies on the stack (knows the sizes of each, so you can copy them).

The return value of f () is placed in a record. Because the compiler knows what is being returned, because the type information is already in the language, you can return it by placing it in a record. In C, with primitive types, the simple act of copying the bits of the value is equivalent to copying the object.

Passage and return of large objects

Consider now the user-defined types. If you create a class and want to pass an object of that class by value, how does the compiler know what to do? The information of the class is not in the compiler, as it has been defined by the user.

To investigate this, you can start with a simple structure that, clearly, is too large to be returned through the records:

//: C11: PassingBigStructures.cpp
struct Big {
char buf [100];
int i;
long d;
} B, B2;

Big bigfun (Big b) {
b.i = 100; // Do something to the argument
return b;
}

int main () {
B2 = bigfun (B);
} ///: ~
Listing 11.5. C11 / PassingBigStructures.cpp

The conversion to assembly code is a bit more complicated because most compilers use “helper” functions instead of inline. In the main () function, the call to bigfun () starts as it should: the content of B is placed on the stack. (Here it could happen that some compilers load records with the address and size of Big and then an auxiliary function is responsible for placing the Big in the stack).

In the previous source code fragment, the only thing necessary before calling the function is to place the arguments on the stack. However, an additional action is seen in the assembly code of PassingBigStructures.cpp: the address of B2 is placed on the stack before calling the function, although obviously it is not an argument. To understand what happens, you need to understand the constraints of the compiler when you call a function.

Battery frame for function calls

When the compiler generates code to call a function, it first places all the arguments on the stack and then makes the call. Within the function, a code is generated to move the stack pointer down and thus provides memory for the local variables within the function. (“Down” is relative, the machine can increase or decrease the stack pointer when placing an argument). But when the assembler CALL is done to call the function, the CPU places the address from which the call is made, and in the assembler RETURN that address is used to return to the point from which the call was made. This direction is sacred, because without it the program would be completely lost. Here is the appearance of the stack frame after executing CALL and putting the local variables of the function:

Calling a function
Figure 11.1. Calling a function

The code generated by the rest of the function expects the memory to have this device

watch, learn and share this video tutorial with your friends and family members.

Leave a Reply

Your email address will not be published. Required fields are marked *