C beginner solution (5) - constructor (middle)
Third, copy constructor
Reasons for existence
When the chef makes cooking, it is always necessary to join a wide range of seasonings, the type of seasoning, and the quantity determines the taste of the dishes; experienced chef is always good at the taste of the customer. Adjust the investment of seasonings to cater to the customer's preferences. We are also in the object of the project: The newly created object can be initialized by our need for a constructor having a parameter table by overloading. For example, for complex class complex, we can specify a solid, imaginary part, which is implemented by "investment" two Double parameters; and we can also "invest" in the constructor. An int value is an initial size of an array object. If necessary, we can repeatedly "sprinkle" an internal array as an initial value of each element, and so on. Well, in short, we can "modulate" it can be prestressed by the need to introduce various parameters from the constructor of the class. Make us have done a dish, oh, no, it has already had an object, such as a plurality of COMPLEX object C, now we want to create a plurality of plural objects D, make it completely equal, what should I wait? do? Oh, with the experience of the next section, I know that you will walk to the computer, knock out similar to the following code:
Complex D (C.Getre (), C.Getim ()); // getre () and getim () return a complex real, imaginary
Very good, it can work correctly, isn't it? However, after I went back and enjoy a few eyes, would you start like this seems to be such a lengthy? Moreover, you should see that the complex class is a relatively simple abstract data type. It provides the public interface that allows us to access all its private member variables, so we can get all the "privacy" data of C to do D. Initialization; but there are quite a few classes, it is not possible to access all of its private objects, step by step, even if it can be fully accessed, our constructor parameter table may not always be fine, meticily portrayed the object, For example, for an object of the INTARRAY class, such as A, it may contain a thousand elements, but we cannot write
INTARRAY COPY_OF_A (A.SIZE (), A [0], A [1], A [2], ..., A [999]); // Sufficient detailed portraint, also stupid enough
Hey, it seems that our mistakes are wrong to portray our objects with a limited number of auxiliary parameters, and it is often inappropriate. To determine the object B by 100% of the object A, the object B must master 100% of the object A, that is, the parameters incorporated in the constructor should include 100% of the object A. Who includes "100% information of object a"? It seems that it seems to be a problem, oh, we seem to make a dizzy, but we should not forget the rules that consider problems from simple aspects, including "100% information of object A" guy. , Should not be a large number of variables, should not be a group of fearful parameters, it should be ... that is the object A itself.
Ah, it is really nonsense. Don't take so much, we now give the complex D initialization immediately equal to the plural C, you can write
Complex d (c); // um ... perfect representation
"Wait ..." You may be reminded, "You seem to have not defined the corresponding constructor."
This is a very kind reminder, but I can tell you with a pleasant mood: C has made the default definition for "constructor" with the same type of object as a unique parameter ", for this default The object created by the constructor will have the same content as the object incorporated as the parameters: In fact, this process is generally carried out in the "bit copy", ie the one of the blocks occupying the parameter object. Content "One Brain" is copied into the memory block where the newly created object is, the result of this makes the newly created objects with the exact same variable value as the parameter object, whether it is public or private. Thus, we have to initialize a new object with an existing object, of course, both should be the same type. You may be interested in "why I can provide such a constructor default". In essence, it is not only the example of "declaration object and initialization" to use this feature, in some more common situations, and it is also the case we don't pay attention to, this feature must be used, for example: Transfer function call. Consider the following code segment:
Void SWAP (Int A, INT B)
{
INT TEMP = A;
A = B;
B = TEMP;
}
int main ()
{
INT x = 1, y = 2;
SWAP (X, Y); // ...
... // Others
Return 0;
}
Question: After the SWAP (X, Y) is executed, how much is the value of x, y? There is no doubt that of course, x == 1, y == 2, because when SWAP is executed, the exchange is not the value of the X, Y variable, but only one copy. We can "new creation" two variables A, B, while A, B are initialized when calling the SWAP function, and A, B are initialized in the value of x, y, respectively:
INT A (X), B (Y);
... // The following operations have nothing to do with X, Y.
Similarly, if there is such a function:
Void Swap (Complex A, Complex B)
{
...
}
When there is a call such as SWAP (P, Q), P, Q is also copied, similar to: Complex A (P), B (Q);
Like the introduction of the parameters, the function is returned (if there is returned), there is only a similar replication operation as long as it returns a reference (here, not considering the compilation optimization of some provincial replication steps).
Therefore, if the system does not define such a special constructor for us, we will not define any parameters that appear in the Complex type, or the return value is a function of the COMPLEX type, which is not reasonable, at least, it is not So is so acceptable.
Since such constructor can be seen as "copy" from a existing same type of object creation a new object, this constructor is also referred to as a copy constructor, or a copy constructor. Senior it There is no difference with other constructors, just:
1. If it is not explicitly given, the system will provide a replication constructor default (similar to the constructor without parameters, as well as the destructor);
2. When the function is transferred or returned, the system will use this constructor (relative to other constructor-called constructor).
噫, the above is the explanation of the copy constructor. Time is not early, the rest. Friends, I wish you good night, see you next ...
Oh, no, it's wrong, it's not so simple. We ... we just started ... Hey, at least I have to finish this article before going to bed, and there seems to be a lot of content, in short, there are quite a lot of factors to consider. Well, you may say that the default replication constructor provided by C does not have durable, a bit is unable to clone the object for us, then the new object is definitely exactly the same as the copy object, what else What should I think about? The problem is on "exactly the same" understanding. We said that two triangles are exactly the same, refers to they have the same edge, corner; we say that two plots are exactly the same, refers to them with corresponding equal, imaginary; we say that two documents are exactly the same, means Their content is meticulous ... then we say that one type of object is exactly the same, of course, from the simple sense, it should refer to these two objects have the same members, including member functions and member variables; However, the same type of objects certainly have the same member function, so it is more specifically that it is identical to the same member variable.
However, the objects of the classes in C are not, nor should it be considered only a series of binary data in a corner in memory, which is the machine's view. In fact, the object is a description of the reality model, which itself should have a certain abstraction. Objects of an Orange class, maybe we will intend to consider them as a living orange when we program, rather than a lively orange. Object-oriented abstraction of C makes us better painting the real model and perform correct, appropriate use. Therefore, when we say that the two objects "are completely equal", based on abstract thinking, we are referred to, or as required, just the meaning of the real model represented by the object, as for the implementation of the binary level, Whether the 0101 inside is the same, it is not our most concerned, of course, in many cases, the two are equal, but not. For example, two intaRay array objects, of course, each containing a complete pointer as a private member to store the first address of the memory space in array. But we say that these two array objects are the same, more meaning referring to them contains the same dimension, size, and corresponding elements, as for the pointer value of the first address, although they are not equal in most cases But this does not prevent we think that two arrays are the same. In this example, the conceptual meaning representative of "object" has a division in the machine level, one of the tasks of the program language is to provide a more appropriate abstraction, so that the program has some kind of agreed to the reality model. Rationality. Therefore, when both are divided, since we use the high-level language with considerable abstract abilities, it should tend to respect the concept of realism, not the return.
Said so much, now I will look at the "full photo" replication mode adopted by default replication constructor will have the contradiction between the "conceptual level" and "Machine Level". Just now, we have caught the tail of the Intarray class. Now, if you use the default replication function representing the "Machine Level", what will happen. Well, it's right, newly created Intarray object - may be called B - will have the original object - may be known as A- exactly the same member variable, of course, also includes a pointer for storing the first address of the array memory space Members, that is, A and B are actually "shared" with the same memory to store the "they". Since then, if I change the value of an element in the A array, the corresponding elements in B will also change, and vice versa - our procedures are happier. More seriously, if we joined the code that releases the array memory in the Intarray's destructor (almost all containers will do this), then because there are multiple objects to share the same memory, this poor memory will It was released many times, and in fact, when our procedures were likely to be collapsed for the second time, this situation is the most typical when the value is transferred. In our conceptual sense, the understanding of "array replication" is to generate a new array that is the same as the elements of the original number group, and the ghost tricks of the shared memory are never shared. But our loyal C compiler does not understand this, after all, it is just a machine, so it will only be used for us to copy the copy. In this way, we have an obligation to tell us the understanding of the concept itself, of course, this way is to define a replication constructor.
2. Discussion on statement
First, take the INTARRAY class as an example, let's take a look at what the replication constructor should look like: The constructor is not returned, and the copy constructor is of course no exception, so we only have to consider parameters. The copy constructor has only one parameter. Since it is incorporated in the same type of object, a natural idea is to use the object as a parameter, like this:
INTARRAY (INTARRAY A);
Unfortunately, even such a simple statement also implies a subtle mistake, huh, let's take a look: When you need to initialize a new object with a value of an IntaRray object, of course, compile The machine searches in each overloaded constructor (if there are multiple words), the version it finds, the discovery declaration parameter is consistent with the incoming object, so the constructor will be called. So far, everything is in our expectations, but the problem is coming soon: the parameter of this function we use the value transfer method, according to the previous analysis, this requires calling the copy constructor, so the compiler is again search again. Finally, it has found it, so it is called, but in the same manner, it is necessary to copy again, then call ... This process is scheduled, and each time it goes to the function entry, it will be recursive until the stack space is exhausted. ,program crash...
Of course, such a good play is not conference in reality, because the compiler will find this obvious problem in time, and report errors, stop compilation; so we have to think about other methods. The reason why we have just got to be awarded is due to the call of the replication constructor, which is the operation that needs to be resolved during transmission - thereby producing infinity recursive. From is the view, the value is not always there; the user I want to pass this time will soon react to the way to pass the value: address delivery (inventive), so the declaration is like this:
INTARRAY (INTARRAY * P);
Only as a general constructor, it should be able to run very well, but don't forget that we have to provide a copy constructor, it is required to accept a similar type of object, like this: INTARRAY A;
... // Operation to A
INTARRAY B (a);
Instead of accepting pointers:
INTARRAY A;
... // Operation to A
INTARRAY B (& A); / / Do you have to take the address? Of course, it can run correctly, but ...
Otherwise, although it is possible to add a snap-residence like the above manner as the above, when the value is passed (or the function returns), the compiler can choose you when you find the right definition. Pointer version.
Since the copy constructor must be an object, but can not pass the value, you cannot replace, solve the plan, I think you will think of it. That's right, use reference:
INTARRAY (INTARRAY & A);
Since the reference is just an alias, it is essentially "original" reference object, so, of course, there will be any parameter copying problem, so it will not call the replication constructor again - - that is, it!
After success, let's see if you can do some improvements: We are in the process of copying the object, of course, from semantics, generally do not change the value of the copied "master object". We are unimaginable, such as:
Complex B (a);
After the statement, the plural A will become unrecognized; at the same time, if the "master object" is a const type, we will mean that we can't use it to initialize other objects: because the parameters Intarray & a cannot guarantee that A will not be modified! By passing the Const constant to non-Const references will result in compilation errors, but this limit is clearly not what we want; and we have the case where we have modified the "master object" of this undesirable change in the replication constructor, and this Everything will be difficult to find it in the future. In summary, we usually add consT before reference to the reference parameters of the copy constructor:
INTARRAY (Const Intarray & A);
This is a more perfect, it avoids the above problems; and even if the non-Const object can also be referenced as a CONST type (as analyzed above, it is not possible).
3. Definition implementation
Ok, we have found a suitable parameter for copying the constructor, and I will tell a paragraph for the discussion of the statement. Now that we still take Interray as an example to see what it should look like. If you read the "Constructor (on)", then remember the statement of Intarray I gave to the previous section? If your answer is yes, then I admire your intentions, but I can't remember, for myself, and those friends who are the same as me, I have not read the friend of the year, I put the previous section. The main part of the INTARRAY declands are pasted as follows (in fact, it is very simple):
Class IntaRray
{
PUBLIC:
... // Others
Private:
INT * _P;
INT _SIZE;
}
As you speculate, IntArray uses an integer pointer _P to store the first address of the memory space assigned by the New Expression on the free storage area, and _Size stores the number of integer elements that can accommodate this space. In words, it also represents the size of the array. With the view of the machine, in Intarray, _p and _size are the identity variables identified by the two Intarray objects (we assume that the PULIC sector is a member function, but there is no member variable), but from our alignment concept The understanding, _size and _p are the same in the memory, and the same meaning is the same meaning; in other words, _size and _P referred to in the content (not the _p stored address value) The information constitutes the array of our "conceptual understanding". Therefore, when copying, based on respect for "conceptual abstraction", the memory space referred to in _P should be copied, not simply copy _P itself. This is what we have to indicate in a custom replication constructor; the following is a possible definition, I put the analysis in the comment section: INTARRAY (Const Intarray & A)
{
_Size = a._size; // We show that the default constructor does not exist after the replication constructor is defined.
// So you must implement all the necessary member variables to copy
If (_size> 0) // only A size is greater than 0, that is, a content is required, only reproduce
{
_P = new int [_size]; // Constructor always calls when creating a new object, so don't forget to allocate memory
For (int i = 0; i <_size; i) // copy the content in A by one
_P [i] = a._p [i];
}
Else
_P = 0; // Safe, we set the pointer of zero content arrays to 0
}
Well, add it again. If your program is very interested, you can use memory copy functions such as memcopy () to copy the memory referred to in the entire A._P, which will be more fast than the use cycle. However, in C , the use of this function does not need to be careful: and a considerable part of the default replication constructor, it is just simply mechanically copying memory, so if you apply this function to an object such as Intarray, I hope "Efficiency" will copy multiple IntArray objects, so almost certainly there will be problems: it will not know what should be copied _P, and thus will generate all kinds of problems mentioned above; even we explicitly define IntArray Copy the constructor, it will not call - because you use it means that you ask "bit copy", not "copy construct". So, in C , only when the Memcopy application is in the built-in basic type, I dare to guarantee that it is safe. Of course, if you are not allowed, then in C , the function left by this C is not a good countermeasure.
4. Replication structure in combination or inheritance
I don't know if you haven't, I just said that Memcopy () mechanical copy is just the same as the "quite part" default replication constructor, oh, it should be in turn, just "quite partial" default replication constructor uses "bit copy" mode. Hey, is there a mysterious power to control another default replication constructor?
First consider the following example: If I want to design a class now, call A, which contains an IntArray object as one of the members, of course, this is not surprising, like this:
Class A
{
PUBLIC:
... // OtherSprivate:
INTARRAY _ARRAY; // That's it
... // Others
}
Then there is a replication structure of A's object in my code, which is also very likely, like this function:
A DOSMETHING (A)
{
... // Some operations
A b (a); // construct B in a replication
...
Return B;
}
The above piece is implied with the copy structure of the A object (do you see?), But if I didn't define a replication constructor for A, as mentioned earlier, the compiler, of course, will establish a default replication structure. Function, then call it when needed. The problem is that this default replication constructor uses a simple bit copy, which can at least conclude our _Array members will suffer. Of course, you may blame me to add a copy constructor to add, but I can say, and it is very reason to argue that the rest of Class A does not need to provide a replication constructor, as for the Intarray type member, I just This class of users, it has a good internal mechanism should not be responsible by me. In fact, I have no responsibility: even if I am willing to design a replication constructor specifically, what should I achieve? As a user, I get the interface of IntArray, that is, I only use it, and I can't care about how it is internally, and the replication constructor usually needs to involve the above.
As a successful language, C certainly cannot allow the above embarrassment. In fact, if a class has a member object that needs to be called (user-defined, it may be indirect) copy constructor, and the class itself does not provide a replication constructor, and its default constructor is not simply use bit copy mode, and It is copied separately, "one-by-one", respectively, and for the object that needs to be called the copy constructor, the corresponding call is performed to maintain the conceptual meaning of the replication structure.
It is also similar to the inheritance, if there is a SuperIntArray class inherits to Intarray:
Class SuperintArray: Public InTARRAY: PUBLIC INTARRAY
{
PUBLIC:
... // Something
Private:
... // Something
}
Even if the SuperIntarray itself does not provide a replication constructor, the structure of the base class Intarray section is also called in the INTARRAY (Const INTARRAY &) when the object needs to be copied.
Well, if the components mentioned earlier are inherited, the new type shows the replication constructor, the corresponding member, or the base class replication constructor is also called. In fact, we have discussed this issue when we mentioned the constructive function, so this part basically doesn't have much new idea: Don't forget, the replication constructor is only one of the constructor.