Don't treat the constructor and the designer function to wait with ordinary functions.
Give you a paragraph, this is a real master answer, you will understand you.
Chapter 9 Constructor, Destructor and Assignment Function Constructor, the destructor and assignment function are the most basic functions of each class. They are too ordinary, which makes it easy to paralyze, in fact, these seemingly simple functions are dangerous as there is no top water.
Each class has only one destructuring function and an assignment function, but there can be multiple constructor (including a copy constructor, others referred to as a normal constructor). For any class A, if you don't want to write the above functions, the C compiler will automatically generate four default functions, such as
A (void); / / default non-parameter constructor
A (Const A & a); // Default copy constructor
~ A (void); / / default destructor
A & Operate = (const A & a); // Default assignment function
This is not forbidden, since it can automatically generate functions, why do you want programmers?
The reason is as follows:
(1) If the "default non-parameter constructor" and "default destructor" are used, it is good for the C inventor Stroustrup to abandon the opportunity of independent "initialization" and "cleaning".
(2) The "default copy constructor" and "default assignment function" are implemented in a "bit copy" rather than "value copy", if the class contains pointer variables, these two functions are destined to be wrong .
For C programmers who don't have a bitter, if he says writes constructor, the destructor and assignment function is easy, you can not use your brains, indicating that his understanding is more superficial, and the level needs to be improved.
In this chapter, in the case of the design and implementation of class String as an example, the in-depth explanation is neglected by many textbooks. The structure of the String is as follows:
Class String
{
PUBLIC:
String (const char * str = null); // ordinary constructor
String (const string & other); // Copy constructor
~ String (void); // Destructor
String & Operate = (const string & other); // assignment function
Private:
Char * m_data; // Used to save strings
}
9.1 Construction Functions The origin of the designer function as a more advanced language than C, C provides a better mechanism to enhance the security of the program. The C compiler has a strict type security check function, which can almost find out all the syntax issues in the program, which has made the programmer's busy. However, the program has passed the compilation check and does not mean that the error does not exist. In the "wrong" big family, the status of "syntax error" can only be a little brother. High-level mistakes are usually hidden, just like a criminal, you can't easily catch him.
Based on experience, many difficult program errors are due to the correct initialization or removal of variables, while initialization and clearing work are easily forgotten. Stroustrup takes into account this problem when designing C languages and resolves well: put the object's initialization work in the constructor, put the clear work in the destructor. When the object is created, the constructor is executed automatically. When the object is dying, the destructor is automatically executed. There is no need to worry about forgetting the initialization and clearance of the object.
The name of the constructor and the destructor cannot be casually, and the compiler can be made automatically. The naming method of Stroustrup is simple and reasonable: the constructor, the destructor function, the same name, due to the opposite of the destructor, and the contrary to the constructor, the prefixed '~' is shown differently. In addition to the name, the other specialty of the constructor and the destructuring function is not the return value type, which is different from the function of the return value type Void. The mission of constructor and the descent function is very clear, just like being born with death, light slippery. If they have a return value type, the compiler will not know what it is. In order to prevent the export branch, simply specify that there is no return value type. (With the above, the literature is referred to [EEKEL, P55-P56])
9.2 Initialization Table constructor of the constructor has a special initialization mode called "Initialization Expression Table" (referred to as initialization table). After the initialization table is behind the function parameter table, it is before the function body {}. This shows that the initialization work in the table occurs before any code in the function body.
Use rules for constructor initialization table:
u If there is a inheritance relationship, the derived class must call the constructor of the base class in its initialization table.
E.g
Class A
{...
A (int x); // a constructor
}
Class B: Public A
{...
B (int x, int y); // B constructor
}
B :: b (int X, int y)
: A (x) // Call a constructor in the initialization table
{
...
}
The CONST constant of the U class can only be initialized in the initialization table because it cannot be initialized in the functional body (see Section 5.4).
The initialization of the Data member of the U class can be used in both initialization tables or functions to assign values, and the efficiency of both ways is not exactly the same.
Members of non-internal data types should be initialized in the first way to obtain higher efficiency. E.g
Class A
{...
A (void); // no parameter constructor
A (const a & other); // copy constructor
A & Operate = (const a & other); // assignment function
}
Class B
{
PUBLIC:
B (Const A & a); // B constructor
Private:
A m_a; // member object
}
In Examples 9-2 (a), the constructor of class B calls the copy constructor of class A in its initialization table, thereby initializing the member object M_A.
In Example 9-2 (b), the constructor of class B initializes member objects M_A in the function body. What we see is just a assignment statement, but actually B constructor dry two things: firstly create M_A objects (call the parameter constructor of A), then call the assignment function of class A, to assign the parameter A Give M_A.
B :: B (Const A & A)
: m_a (a)
{
...
}
B :: B (Const A & A)
{
m_a = a;
...
}
Example 9-2 (a) Member Object Inly in Initialization Table Example 9-2 (b) member object is initialized in the function body
For data members of internal data types, the efficiency of both initialization methods has little difference, but the latter's program seems to be clearer. If the declaration of class f is as follows:
Class F
{
PUBLIC:
F (int x, int y); // constructor
Private:
INT M_X, M_Y;
INT M_I, M_J;
}
The constructor of f in Example 9-2 (c) The first initialization method is used, and the constructor of F in Example 9-2 (d) is a second initialization method. F :: f (int x, int y)
: m_x (x), m_y (y)
{
m_i = 0;
m_j = 0;
}
F :: f (int x, int y)
{
m_x = x;
m_y = y;
m_i = 0;
m_j = 0;
}
Example 9-2 (c) Data Members are initialized in the initialization table, and the data member is initialized in the function body.
9.3 Submersible Structure Start from the most root of the class level, in each layer, first call the constructor of the base class, then call the constructor of the member object. The sequence is performed in strict accordance with the instead of constructing, and the order is unique, otherwise the compiler will not automatically perform the destructure process.
An interesting phenomenon is that the order of the initialization of member objects is completely not affected by their order in the initialization table, only by the order of the member objects declared in the class. This is because the statement of the class is unique, and the class constructor can have multiple, so there will be a plurality of different order initialization tables. If the member object is constructed in the order of the initialization table, this will cause the destructuring function that cannot be unique. [Eckel, p260-261]
9.4 Example: Constructor of Class String and the Ordinary Construction Function of the Destructor // String
String :: String (const char * str)
{
IF (str == NULL)
{
m_data = new char [1];
* m_data = '/ 0';
}
Else
{
INT length = Strlen (STR);
m_data = new char [length 1];
STRCPY (M_Data, STR);
}
}
// String destructor
String :: ~ String (void)
{
delete [] m_data;
// Since M_DATA is the internal data type, it is also possible to write a delete m_data;
}
9.5 Do not despise the copy constructor and assignment function Since the copy constructor and assignment function are not all objects, the programmer may look at some of these two functions. Please remember the following warnings, you will be careful when reading your text:
U This chapter says that if you do not actively write a copy constructor and assignment function, the compiler will automatically generate the default function in the "bit copy" mode. If the class is included in the pointer variable, the two default functions implies an error. With the two objects A, B, B, B as an example, assuming that A.M_DATA is "Hello", B.M_Data's content is "world".
The "bit copy" that is assigned to b, the "bit copy" of the default assignment function means executing B.M_DATA = A.m_Data. This will cause three errors: First, the original memory of B.m_data is not released, resulting in memory disclosure; second, B.M_Data and A.m_Data points to the same block, A or B changes will affect the other party; three When the object is analyzed, m_data is released twice.
u Copy constructor and assignment function are very easy to confuse, often leading to wrong, misuse. The copy constructor is called when the object is created, and the assignment function can only be called in the already existing object. In the following procedures, the third statement and the fourth statement are very similar. Which one calls the copy constructor, which calls the assignment function?
String a ("Hello");
String B ("World");
String c = a; // Call the copy constructor, it is best to write c (a);
C = B; // Call the value of the third statement in this example, should be changed to String C (a) to distinguish between the fourth statement.
9.6 Example: Copy constructor of class string and assignment function / / copy constructor
String :: string (const string&)
{
/ / Allow Other private members M_Data
INT length = strlen (other.m_data);
m_data = new char [length 1];
STRCPY (m_data, other.m_data);
}
// Assignment function
String & string :: Operate = (const string & other)
{
// (1) Check the self-assignment
IF (this == & other)
RETURN * THIS;
// (2) Release the original memory resources
delete [] m_data;
/ / (3) Assign new memory resources and copy content
INT length = strlen (other.m_data);
m_data = new char [length 1];
STRCPY (m_data, other.m_data);
/ / (4) Return to the reference to this object
RETURN * THIS;
}
The difference between the class String copy constructor and the ordinary constructor (see Section 9.4) is that there is no need to compare with NULL at the entry, because "reference" cannot be NULL, and "pointer" can be null.
The assignment function of class String is much more complicated than the constructor, and the four steps are implemented:
(1) First step, check the self-assignment. You may think more about it, is it stupid to write a self-assignment statement like A = A! It doesn't. But indirect self-assignment still may appear, for example
// content self assignment
B = a;
...
C = B;
...
A = C;
// Address self-assignment
B = & a;
...
a = * b;
Maybe someone will say: "Even if you have a self-assignment, I can ignore it, I don't make some time to let the object copy yourself, anyway, I won't be wrong!"
He really said wrong. Look at the second step DELETE, can you copy yourself after committing suicide? So, if you find a self-assignment, you should immediately terminate the function. Be careful not to check the IF statement of your own value
IF (this == & other)
Wrong writing
IF (* this == taher)
(2) Second step, release the original memory resources with Delete. If it is not released now, there is no chance in the future, which will cause memory leakage.
(3) Step 3, allocate new memory resources, and copy strings. Note that the function strlen returns the valid string length and does not contain end values '/ 0'. The function strcpy is copied together with '/ 0'.
(4) The fourth step, returns the reference to this object, and the purpose is to implement chain expression such as A = B = C. Be careful not to write Return * this to Return this. So can you write RETURN OTHER? Is the effect not the same?
No! Because we don't know the lifetime of the parameter Other. It is possible that other is a temporary object, and it immediately disappears after the assignment, then returnit is returned to the garbage.
9.7 Laying method Processing copy constructor and assignment function If we don't want to write a copy constructor and assignment function, it is not allowed to use the default function generated by the compiler. The lazy way is: just declare the copy constructor and assignment function as a private function, no need to write code.
E.g:
Class A
{...
Private:
A (Const A & a); // Private copy constructor
A & OPERATE = (const A & a); // Private assignment function
}
If someone tries to write the following procedures:
A b (a); // Call the private copy constructor
B = a; // Call the private assignment function
The compiler will point out errors because the outside world cannot operate A private function.
9.8 How to implement the constructor of the basic function base class in the derived class, the destructuring function, and the assignment function cannot be derived. If there is a inheritance relationship between the class, you should pay attention to the following when writing the above basic functions:
u Deportation class constructor should call the constructor of the base class in its initialization table.
u Base class and derived sectors should be empty (ie, the Virtual Keyword). E.g
#include
Class Base
{
PUBLIC:
Virtual ~ base () {cout << "~ base" << endl;}
}
Class Derived: Public Base
{
PUBLIC:
Virtual ~ derived () {cout << "~ derived" << endl;}
}
Void main (void)
{
Base * pb = new deerived; // upcast
Delete PB;
}
The output is:
~ Derived
~ Base
If the destructor is not imaginary, the output result is
~ Base
u When writing the assignment function of the derived class, be careful not to re-assapose the data member of the base class. E.g:
Class Base
{
PUBLIC:
...
Base & Operate = (const base & ta); // class base assignment function
Private:
INT M_I, M_J, M_K;
}
Class Derived: Public Base
{
PUBLIC:
...
Derived & Operate = (const derived & other); // class Derived assignment function
Private:
INT M_X, M_Y, M_Z;
}
Derived & Derived :: Operate = (const derived&)
{
// (1) Check the self-assignment
IF (this == & other)
RETURN * THIS;
/ / (2) Re-assapose data members of the base class
Base :: Operate = (other); // Because you can't directly operate private data members
// (3) Assignment of data members of the school
m_x = other.m_x;
m_y = other.m_y;
m_z = other.m_z;
/ / (4) Return to the reference to this object
RETURN * THIS;
}
9.9 Some experiences have some C programming books called constructor, destructor and assignment functions are class "BIG-Three", which is indeed any of the most important functions, which is not despised. Maybe you think the content of this chapter is enough, learn to be safe, I can't make this guarantee. If you want to eat "Big-Three", please read the reference [cline] [meyers] [murry].