High-quality C ++/C Programming Guide-class constructor, destructor, and assignment function

Source: Internet
Author: User

Class constructor, destructor, and value assignment function

Constructors, destructor, and assignment functions are the most basic functions of each class. They are so common that they are easy to paralyze. In fact, these seemingly simple functions are as dangerous as the sewers without the top cover.

Each class has only one destructor and one value assignment function, but it can have multiple Constructors (including one copy constructor, and others are called normal constructor ). If you do not want to write the above functions for any class A, the C ++ compiler will automatically generate four default functions for Class A, such

A (void); // default no-parameter Constructor

A (const A & a); // default copy constructor

~ A (void); // default destructor

A & operate = (const A & a); // default value assignment function

This is confusing. Why do programmers need to write functions automatically?

The reason is as follows:

(1) If "Default non-parametric constructor" and "Default destructor" are used, the chance of independent "initialization" and "Clearing" is abandoned, the good intentions of C ++ inventor Stroustrup are in vain.

(2) Both the "Default copy constructor" and "Default Value assignment function" are implemented by "bit copy" instead of "value copy, if the class contains pointer variables, these two functions are doomed to go wrong.

For C ++ programmers who have not suffered enough, if he says it is easy to write constructor, destructor, and assignment function, he does not have to worry about it, indicating that his understanding is superficial, the level needs to be improved.

This chapter takes the design and implementation of the String class as an example to explain in depth the principles ignored by many textbooks. The String structure is as follows:

Class String

{

Public:

String (const char * str = NULL); // common Constructor

String (const String & other); // copy the constructor

~ String (void); // destructor

String & operate = (const String & other); // value assignment function

Private:

Char * m_data; // used to save strings

};

9.1 The origins of constructor and destructor

As a more advanced language than C, C ++ provides a better mechanism to enhance program security. The C ++ compiler has a strict type security check function, which can almost find all the Syntax problems in the program, which indeed helps programmers. However, after the program passes the compilation check, it does not mean that the error does not exist. In the "error" family, the "syntax error" status can only be regarded as a younger brother. High-level errors are often hidden in depth, just like a sly criminal. It is not easy to catch him.

Based on experience, many program errors that are hard to detect are caused by the variable being correctly initialized or cleared, and initialization and cleanup are easily forgotten. Stroustrup fully considers this problem when designing the C ++ language and solves it well: Put the initialization work of the object in the constructor, and put the cleanup work in the destructor. When an object is created, the constructor is automatically executed. When an object dies, the Destructor is automatically executed. You don't have to worry about object initialization and cleanup.

The name of the constructor and the Destructor cannot start at will, and must be recognized by the compiler before it can be automatically executed. The naming method of Stroustrup is simple and reasonable: the constructor and destructor have the same name as the class. because the purpose of the constructor is opposite to that of the constructor, the prefix '~ 'To show the difference.

Except for the name, the constructor and the Destructor have no return value type, which is different from the void function. The mission of constructor and destructor is very clear, just like birth and death. If they have return value types, the compiler will be overwhelmed. In order to prevent out-of-the-box branches, it is simply specified that there is no return value type.

9.2 initialization table of the constructor

The constructor has a special initialization method called "initialization expression table" (called initialization table ). The initialization table is located after the function parameter table, but before the function body. This indicates that the initialization work in the table occurs before any code in the function body is executed.

Rules for using constructors to initialize tables:

If the class has an inheritance relationship, the derived class must call the base class constructor in its initialization table.

For example

Class

{...

A (int x); // constructor of

};

Class B: public

{...

B (int x, int y); // constructor of B

};

B: B (int x, int y)

: A (x) // call the constructor of A in the initialization table

{

...

}

The const constant of the class can only be initialized in the initialization table, because it cannot be initialized in the function body by assigning values (see section 5.4 ).

Class data members can be initialized using the initialization table or function body assignment method, the efficiency of these two methods is not exactly the same.

Non-Internal data type member objects should be initialized in the first way to achieve higher efficiency. For example

Class

{...

A (void); // No parameter Constructor

A (const A & other); // copy the constructor

A & operate = (const A & other); // value assignment function

};

Class B

{

Public:

B (const A & a); // constructor of B

Private:

A M_a; // member object

};

In Example 9-2 (a), the constructor of Class B calls the copy constructor of Class A in its initialization table to initialize the member object M_a.

In Example 9-2 (B), the constructor of Class B initializes the member object M_a by assigning values in the function body. We only see a value assignment statement, but in fact, the constructor of B does two things: Create the M_a object in the dark (call the non-parameter constructor of ), call the value assignment function of Class A to assign parameter A to M_a.

B: B (const A &)

: M_a ()

{

...

}

Example 9-2 (a) The member object is initialized in the initialization table

B: B (const A &)

{

M_a =;

...

}

Example 9-2 (B) the member object is initialized in the function body

For data members of the internal data type, the efficiency of the two initialization methods is almost the same, but the latter program layout seems 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;

}

In Example 9-2 (c), the F constructor adopts the first initialization method. In Example 9-2 (d), the F constructor adopts the second initialization method.

F: F (int x, int y)

: M_x (x), m_y (y)

{

M_ I = 0;

M_j = 0;

}

Example 9-2 (c) The data member is initialized in the initialization table

F: F (int x, int y)

{

M_x = x;

M_y = y;

M_ I = 0;

M_j = 0;

}

Example 9-2 (d) The data member is initialized in the function body

 9.3 structure and structure order

The construction starts from the root of the class hierarchy. In each layer, the base class constructor is called first, and then the member object constructor is called. The structure is executed strictly in the order opposite to the structure. This order is unique. Otherwise, the compiler cannot automatically execute the structure process.

An interesting phenomenon is that the initialization order of member objects is not affected by their order in the initialization table. It is determined only by the Order declared by the member objects in the class. This is because the class declaration is unique, and the class constructor can have multiple, so there will be multiple initialization tables in different order. If the member object is constructed in the order of the initialization table, the Destructor cannot obtain a unique reverse order.

9.4 example: constructor and structure of the class StringFunction

// String Constructor

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;

// Because m_data is an internal data type, it can also be written as delete m_data;

}

9.5 do not underestimate the copy constructor and value assignment function

Since not all objects use copy constructor and value assignment function, programmers may despise these two functions. Remember the following warning first, and read the text with caution:

As mentioned at the beginning of this chapter, if you do not actively write the copy constructor and value assignment function, the compiler will automatically generate 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: B. m_data memory is not released, causing memory leakage; B. m_data and. m_data points to the same memory. Changes to either of a or B will affect the other. Third, m_data is released twice when the object is destructed.

The copy constructor and the value assignment function are very confusing and often lead to incorrect writing and usage. A copy constructor is called when an object is created, and a value assignment function can only be called by an existing object. In the following program, the third statement is very similar to the fourth statement. Do you know which one calls the copy constructor and which one calls the value assignment function?

String a ("hello ");

String B ("world ");

String c = a; // call the copy constructor. It is best to write it as c ();

C = B; // The Value assignment function is called.

In this example, the third statement has a poor style and should be rewritten to String c (a) to distinguish it from the fourth statement.

9.6 example: copy constructor and assignment of the String classFunction

// Copy the constructor

String: String (const String & other)

{

// Allow the operation of the private member m_data of other

Int 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 the auto-assigned Value

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 of this object

Return * this;

}

The difference between a String-like copy constructor and a common Constructor (see section 9.4) is that there is no need to compare the constructor with NULL at the function entrance, because "reference" cannot be NULL, the "Pointer" can be NULL.

The value assignment function of the String type is much more complex than the constructor. It can be implemented in four steps:

(1) first, check the auto-assigned value. 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

// Self-assigned content

B =;

...

C = B;

...

A = c;
// Address Auto-assigned Value

B = &;

...

A = * B;

Some people may say, "even if there is a self-assigned value, I can ignore it. It's just a big deal to let the object copy itself. It won't go wrong anyway !"

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.

Be sure not to check the if statement if (this = & other) of the self-assigned Value)

If (* this = other) by mistake)

(2) Step 2: Use delete to release the 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.

9.7 lazy solution to copy constructor and assign value function

What if we really don't want to write a copy constructor and a value assignment function, and do not allow others to use the default function generated by the compiler?

The method of laziness is to declare the copy constructor and the value assignment function as a private function without writing code.

For 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); // The private copy constructor is called.

B = a; // The private value assignment function is called.

The compiler will indicate an error because the outside world cannot operate the private function of.

9.8 how to implement the basics of a class in a derived classFunction

Constructors, destructor, and assignment functions of the base class cannot be inherited by the derived class. If there is an inheritance relationship between classes, pay attention to the following when writing the above basic functions:

The constructor of the derived class should call the constructor of the base class in its initialization table.

The destructor of the base class and the derived class should be virtual ).

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.