Clause 11: declare a copy constructor and a value assignment operator for classes requiring dynamic memory allocation
What is the reason for this clause?
If you create a class without doing anything, the class will create a default constructor, default destructor, default copy function and default value assignment function for you.
So the problem is caused by the default, especially the default copy function and the default value assignment function.
What will the default copy function do? For a = B, it will copy the members of B to another a in a bit. If the memory is dynamically allocated through the pointer, then, only the pointer value is assigned to.
This will cause at least two problems:
First, the memory that B once pointed to will never be deleted, so it will be lost forever. This is a typical example of Memory leakage. Second, now the pointers in a and B point to the same string, as long as one of them leaves its living space, the Destructor will delete the memory that another pointer points.
See the following code:
[Cpp]
# Include <iostream>
# Include <stdlib. h>
# Include <string. h>
Using namespace std;
Class MyString
{
Public:
MyString (const char * value );
~ MyString ();
Friend ostream & operator <(ostream & OS, MyString & c );
Private:
Char * data;
};
MyString: MyString (const char * value)
{
If (value ){
Data = new char [strlen (value) + 1];
Strcpy (data, value );
}
Else {
Data = new char [1];
Data = '\ 0 ';
}
}
Ostream & operator <(ostream & OS, MyString & c)
{
OS <c. data;
Return OS;
}
MyString ::~ MyString ()
{
Delete [] data;
}
Int main ()
{
MyString a ("hello ");
{
MyString B ("world ");
B =;
}
MyString c =;
Cout <c <endl;
}
# Include <iostream>
# Include <stdlib. h>
# Include <string. h>
Using namespace std;
Class MyString
{
Public:
MyString (const char * value );
~ MyString ();
Friend ostream & operator <(ostream & OS, MyString & c );
Private:
Char * data;
};
MyString: MyString (const char * value)
{
If (value ){
Data = new char [strlen (value) + 1];
Strcpy (data, value );
}
Else {
Data = new char [1];
Data = '\ 0 ';
}
}
Ostream & operator <(ostream & OS, MyString & c)
{
OS <c. data;
Return OS;
}
MyString ::~ MyString ()
{
Delete [] data;
}
Int main ()
{
MyString a ("hello ");
{
MyString B ("world ");
B =;
}
MyString c =;
Cout <c <endl;
}
The output result is as follows:
Zookeeper # a zookeeper #
Aborted (core dumped)
We can see that c cannot get the correct value, and the Destructor crashes when calling it.
Therefore, if you call the copy constructor and assign value functions, you must declare them as private ones.
Then let's take a look at how to declare them, as shown below:
[Cpp]
MyString: MyString ()
{
Data = NULL;
}
MyString: MyString (MyString & myString)
{
If (this = & myString)
{
Return;
}
Else
{
If (myString. data! = NULL)
{
Data = new char [strlen (myString. data)];
Strcpy (data, myString. data );
}
Else
{
Data = new char [1];
Data = '\ 0 ';
}
}
}
MyString & MyString: operator = (const MyString & myString)
{
If (this = & myString)
{
Return * this;
}
Else
{
Delete [] data;
If (myString. data! = NULL)
{
Data = new char [strlen (myString. data)];
Strcpy (data, myString. data );
}
Else
{
Data = new char [1];
Data = '\ 0 ';
}
}
Return * this;
}
MyString: MyString ()
{
Data = NULL;
}
MyString: MyString (MyString & myString)
{
If (this = & myString)
{
Return;
}
Else
{
If (myString. data! = NULL)
{
Data = new char [strlen (myString. data)];
Strcpy (data, myString. data );
}
Else
{
Data = new char [1];
Data = '\ 0 ';
}
}
}
MyString & MyString: operator = (const MyString & myString)
{
If (this = & myString)
{
Return * this;
}
Else
{
Delete [] data;
If (myString. data! = NULL)
{
Data = new char [strlen (myString. data)];
Strcpy (data, myString. data );
}
Else
{
Data = new char [1];
Data = '\ 0 ';
}
}
Return * this;
}
Note the following points:
1 MyString c = a; in fact, MyString: MyString (MyString & myString) is called, rather than the function that overload the = sign.
You can use vs or gdb for single-step debugging. The reason is that this form is initialization, not a value assignment.
2. The following two values are assigned values.
MyString B ("world ");
MyString c;
B =;
C =;
At this time, you need to delete the previous data first. Considering that the parameter is sometimes 0, you can define a constructor with the parameter 0, or write the above constructor as MyString :: the default parameter format of MyString (const char * value = NULL.
3. When copying constructors, you do not need to delete data, because this-> data certainly does not apply for space when copying constructors, and deletion may cause errors.
4. In fact, the above Code has two errors: the requested space is strlen (data) + 1, and the last digit is '\ 0 ';
I can run it in cygwin, but it is probably lucky, but the running in vs will crash.
In addition, you can practice how to write this function, which is frequently asked during interviews. A real understanding requires a process with many details: To determine whether data is equal, pay attention to when data can be deleted and when data cannot be deleted, note the correspondence between new [] and delete.
Clause 12: Use initialization whenever possible instead of assigning values in the constructor
The introduction in the book is very clear. Here I will mainly summarize,
Benefits of initialization:
1 const and reference must be initialized, but cannot be assigned a value.
2. high efficiency.
Initialize the class members.
The assignment in the constructor has two steps: 1. Call the default constructor, and 2. Call the assignment constructor.
The constructor of a class is essentially a function, and the function has a form parameter and an actual parameter.
For example
[Cpp]
Class
{
Pulic:
A (B & bInput );
Private:
B B;
}
A: A (B & bInput)
{
B = bInput;
}
Class
{
Pulic:
A (B & bInput );
Private:
B B;
}
A: A (B & bInput)
{
B = bInput;
}
What is its process? Call B () to generate B first, and then call the operator = value assignment function. Of course, the efficiency cannot be guaranteed. This is also the first reason. If it is const or reference, it is declared, but not initialized, then an error is certainly returned during compilation.
Clause 13: the members in the initialization list are listed in the same order as they are declared in the class.
For example:
[Cpp]
# Include <iostream>
Using namespace std;
Class
{
Public:
A (int value );
Void print ();
Private:
Int I;
Int j;
};
A: A (int value): j (value), I (j)
{
}
Void A: print ()
{
Cout <I <"" <j <endl;
}
Int main ()
{
A a (10 );
A. print ();
}
# Include <iostream>
Using namespace std;
Class
{
Public:
A (int value );
Void print ();
Private:
Int I;
Int j;
};
A: A (int value): j (value), I (j)
{
}
Void A: print ()
{
Cout <I <"" <j <endl;
}
Int main ()
{
A a (10 );
A. print ();
}
What is the output result?
Some people think it is 10
The result on my computer is
2281060 10
The first number is a random number, because the execution order is j (I) And then j (10)
Clause 14: Determine whether the base class has a virtual destructor
I have an article dedicated to this. You can check it out.
Http://blog.csdn.net/mlkiller/article/details/8884321
It will not be expanded here.
Clause 15: Let operator = return * this reference
I just wrote the previous example. When I reload the symbols <and =, I still want to write the returned values.
<The symbol "yes" and "=" are both binary operators, but they are different from the following parameters. <It follows two parameters: Stream object and operation object, and finally returns Stream object.
And = only oneself. The returned value should be the object itself. I am struggling with how to return the reference and what is the relationship between it and the this pointer.
When I know the answer, I still have some questions. * Is this equal to & reference? Let's look at an example.
[Cpp]
Int p = 1;
Int * q = & p;
Int & t = p;
Int & s = * q;
Int p = 1;
Int * q = & p;
Int & t = p;
Int & s = * q;
You can see how the reference itself is initialized with a pointer.
I have not made it too clear about the returned const type in the text. It is too late to understand it and I will write it later.