Note: classes that contain dynamically assigned members should provide copy constructors and overload the "=" value assignment operator.
The following examples will be used:
Class cexample {public: cexample () {pbuffer = NULL; nsize = 0 ;}~ Cexample () {Delete pbuffer;} void Init (int n) {pbuffer = new char [N]; nsize = N;} PRIVATE: char * pbuffer; // The class object contains a pointer pointing to the dynamically allocated memory resource int nsize ;};
The main feature of this class is to include pointers to other resources.
Pbuffer points to a memory space allocated in the heap.
I. copy constructors
Int main (INT argc, char * argv []) {cexample theobjone; theobjone. init40); // now another object is required, and the State cexample theobjtwo = theobjone;...} must be initialized ;...}
Statement "cexample theobjtwo = theobjone;" use theobjone to initialize theobjtwo.
The complete method is to copy the values of all members in the memory.
After completion, theobjtwo. pbuffer = theobjone. pbuffer.
That is, they will point to the same place. Although the pointer is copied, the space to which it points is not copied, but shared by two objects. In this way, the objects are not independent, and the deletion of spaces poses a risk.
Therefore, necessary measures are required to avoid such situations.
Review the specific process of the following statement: first, create the object theobjtwo, call its constructor, and then the Member is copied.
You can add operations in the constructor to solve the problem of pointer members.
Therefore, in addition to providing default constructor, C ++ also standardizes another special constructor: copy constructor. In the preceding statement, if a copy constructor is defined in the class, when the object is created, the copy constructor is called. In the copy constructor, you can refer to the input variables, copy the resource pointed to by the pointer.
The format of the copy constructor is: constructor name (Object reference)
The cexample class provided after the copy constructor is defined:
Class cexample {public: cexample () {pbuffer = NULL; nsize = 0 ;}~ Cexample () {Delete pbuffer;} cexample (const cexample &); // copy the constructor void Init (int n) {pbuffer = new char [N]; nsize = N ;} PRIVATE: char * pbuffer; // class object contains pointer, pointing to the dynamically allocated memory resource int nsize ;}; cexample: cexample (const cexample & rightsides) // copy the definition of the constructor {nsize = rightsides. nsize; // copy the regular member pbuffer = new char [nsize]; // copy the content memcpy (pbuffer, rightsides. pbuffer, nsize * sizeof (char ));}
In this way, when a new object is defined and initialized with an existing object, cexample (const cexample & rightsides) will be called, and the existing object will be passed to the constructor using the alias rightsides for copying.
In principle, a copy constructor should be provided for all classes that contain dynamically assigned members.
Another method of calling the copy constructor.
When an object is directly transmitted as a parameter to the function, the function will create a temporary copy of the object. This copy process will also be called the same copy constructor. For example:
Bool testfunc (cexample OBJ); testfunc (theobjone); // The object is directly used as a parameter. Bool testfunc (cexample OBJ) {// operations on OBJ are actually performed on temporary copies after replication}
Another case is also related to temporary objects.
When a local object in the function is returned to the function caller, a temporary copy of the local object will also be created, and the copy constructor will also be called.
CTest func(){ CTest theTest; return theTest}
Ii. overload of value assignment
The following code is similar to the previous example.
Int main (INT argc, char * argv []) {cexample theobjone; theobjone. init (40); cexample theobjthree; theobjthree. init (60); // now an object Value assignment operation is required. The original content of the assigned object is cleared and filled with the content of the right object. Theobjthree = theobjone; // assign a return 0 to an existing object ;}
"=" Is also used, but it is different from the example in "1.". In the example of "1.", "=" indicates initialization in the object declaration statement. More often, such initialization can also be represented by parentheses.
For example, cexample theobjone (theobjtwo );
In this example, "=" indicates the value assignment operation. Copy the content of theobjone to theobjthree, which involves discarding the original content of theobjthree and copying new content.
However, the default operation of "=" only copies the value of the member variable. Old values are naturally discarded.
Because the object contains pointers, it will cause adverse consequences: the pointer value is discarded, but the content pointed to by the pointer is not released (delete ). The pointer value is copied, but the content indicated by the pointer is not copied.
Therefore, in addition to providing a copy constructor, classes that contain dynamically assigned members should also consider overloading the "=" value assignment operator symbol.
Class Definition changed:
Class cexample {... cexample (const cexample &); // copy the constructor cexample & operator = (const cexample &); // overload the value assignment ...}; // The Value assignment operator overload cexample & cexample: Operator = (const cexample & rightsides) {nsize = rightsides. nsize; // copy the regular member char * temp = new char [nsize]; // copy the content memcpy (temp, rightsides. pbuffer, nsize * sizeof (char); Delete [] pbuffer; // Delete the content pointed to by the original pointer (put the delete operation behind it to avoid the special case of X = x, content loss) pbuffer = temp; // creates a new point to return * This}
3. Copy the code that uses the value assignment operator to overload the constructor.
Cexample: cexample (const cexample & rightsides) {pbuffer = NULL; * This = rightsides // "= "}
----------------------------------------------------------------------------------
If you do not write the copy constructor and value assignment function, the compiler automatically generates the default function in the "bit copy" mode. If the class contains pointer variables, the two default functions are implicitly incorrect.
Take the two objects A and B in the string type as an example. Assume that the content of A. m_data is "hello", and the content of B. m_data is "world ". Now assign A to B. The "bit copy" of the default value assignment function means that B. m_data = A. m_data is executed. This will cause three errors:
1) B. m_data, the original memory is not released, resulting in Memory leakage;
2) B. m_data and A. m_data point to the same memory. Any change of A or B will affect the other;
3) m_data is released twice when the object is destructed.
Copy constructor and value assignment function of the string type
// Copy the constructor string: string (const string & Other) {// Private member m_dataint length = strlen (Other. m_data); m_data = new char [Length + 1]; strcpy (m_data, other. m_data) ;}// value assignment function string & string: operate = (const string & Other) {// (1) Check auto-assigned if (this = & other) return * This; // (2) release the original memory resource Delete [] m_data; // (3) allocate new memory resources and copy the content int length = strlen (Other. m_data); m_data = new char [Length + 1]; strcpy (m_data, other. m_data); // (4) return the reference return * This ;}
The difference between a string-like copy constructor and a common constructor is that there is no need to compare it with null at the function entrance, because "Reference" cannot be null, and "Pointer" can be null. The value assignment function of the string type is much more complex than the constructor. It is implemented in four steps: (1) The first step is to check the self-assignment. You may think that, in this case, someone will be stupid enough to write an auto-assigned statement like a =! No. However, indirect auto-assigned values may still appear. For example, the content auto-assigned value B = ;... C = B ;... A = C; // address self-assigned B = & ;... A = * B; some people may say, "even if there is a self-assigned value, I can ignore it. If it is a big deal, let the object copy itself. It will not make any mistakes !" He is really wrong. Check the delete in step 2. Can I copy myself after suicide? Therefore, if the auto-assigned value is found, the function should be terminated immediately. Do not mistakenly write the IF (this = & Other) statement that checks the self-assigned values into if (* This = Other) (2) Step 2, use Delete to release original memory resources. If it is not released now, there will be no chance in the future, which will cause memory leakage. (3) Step 3: allocate new memory resources and copy strings. Note that the strlen function returns a Valid String Length, excluding the terminator '/0 '. The strcpy function is connected to '/0' and copied together. (4) Step 4: return the reference of this object to implement chained expressions like a = B = C. Do not write the return * this error as return this. Can it be written as return other? Isn't the same effect? No! Because we do not know the lifecycle of the parameter Other. It is possible that other is a temporary object, and it disappears immediately after the assignment is complete, then return other will return garbage. If we really don't want to write a copy constructor or a value assignment function and do not allow others to use the default function generated by the compiler, what should we do if we are lazy? The method of laziness is to declare the copy constructor and the value assignment function as a private function without writing code. Example: Class {... PRIVATE: A (const A & A); // private copy constructor A & operate = (const A & A); // Private value assignment function }; if someone tries to write the following program: a B (a); // calls the private copy constructor B = A; // the compiler that calls the private value assignment function will indicate an error, because the outside world cannot operate a's private function.
When initializing an instance of a class, that is, the constructor is called (if other instances are used for initialization, the copy constructor is called ), when this instance is not initialized, the value assignment operator is called.
Function syntax for another overload assignment operator:
# Include <iostream. h> # include <string. h> struct node {char * Name; int age; node (char * n = "", int A = 0) {name = new char [strlen (n) + 1]; strcpy (name, n); age = A;} node (const node & N) // copy constructor {name = new char [strlen (N. name) + 1]; strcpy (name, N. name); age = n. age;} node & operator = (const node & N) // operator '= 'overload {If (this! = & N) {If (name! = 0) Delete [] Name; name = new char [strlen (N. name) + 1]; strcpy (name, N. name); age = n. age ;}return * This ;}}; void main () {node node1 ("Roger", 20); node node2; node2 = node1; // call the reloaded value assignment operator strcpy (node2.name, "Wendy"); node2.age = 30; cout <node1.name <''<node1.age <'' <node2.name <''<node2.age <Endl ;}