Some research on C ++ initialization

        When an object gets a specific value when it is created, we say that the object is initialized. Initialization is not assignment, the meaning of initialization is to create a variable to give it an initial value, and the meaning of assignment is to erase the current value and replace it with a new value. Object initialization can be divided into default initialization, direct initialization, copy initialization and value initialization.

        For variables of the C ++ built-in type, the compiler will give a default value when it is defined outside the function, and the variables inside the function must be initialized before they can be used. For user-defined types, you can do it through the constructor. The constructor is mainly the default constructor and the copy constructor. The following is some discussion on the initialization of the class.

1. Class constructor

       1) The default constructor, class name ();

        2) copy constructor;

Class initialization method:

        1. The first case: XX a; ​​// Declare and define the class directly;

        2. The first case: XX aa = XX (); // directly use the class to live and define;

        3. The second case: XX aa = a; // Declare and initialize with a known object;

        4. The second case: XX aa (a); // Declare and initialize with known objects;

        5. The third case: extern fun (XX aa); // fun (a) function call 

        6. The fourth case: XX fun () {...}; // XX a = fun (); when the function returns a value 

Below we use from to verify:

#include <iostream>
#include <string.h>
using namespace std;
class ClassTest{
	char s[10];
public:
	ClassTest(){
		s[0] = '\0';
		cout << "default constructor" << endl;
	}
	ClassTest& operator = (const ClassTest& st){
		strcpy(s, st.s);
		cout << "this is assignment operator" << endl;
		return *this;
	}
	ClassTest(const char *pc){
		strcpy(s, pc);
		cout << "this is setting constructor" << endl;
	}
	ClassTest(const ClassTest &copyClass){
		strcpy(s, copyClass.s);
		cout << "call copy constructor" << endl;
	}

};
int main() {
	cout << "Initialize Class Analyze:" << endl;
	ClassTest a;  // 1
	ClassTest b = ClassTest(); //2
	ClassTest c = b; //3
	ClassTest d(b);  //4
	ClassTest e("aa"); //5
	ClassTest f = "a"; //6
	return 0;
}

Output result:

Initialize Class Analyze:
default constructor  //1
default constructor  //2
call copy constructor  //3
call copy constructor  //4
this is setting constructor  //5
this is setting constructor  //6

It can be seen from the results: although 2 and 1 contain an equal sign, so some people think that the overload of the symbol will be called, but from the result, it can be seen that the default constructor is called for both 1 and 2, and the same for 5 and 6 In case, the non-default constructor is called, and 3 and 4 both call the copy constructor. When calling overloading, it is not applied in the initialization situation, but in the process of assignment, for example, we increase f = a; then this time will call the symbol overload.

2. The difference between initialization list, constructor and = assignment

This part is reproduced from: https://www.cnblogs.com/arxive/p/8418187.html

It is well known that when a C ++ object is created, a series of initialization work is performed by the constructor. From the perspective of a single class with no inheritance relationship, in addition to the generation and specification of the constructor itself, it also involves some details such as initialization steps and member initialization methods. This note mainly introduces these details to clarify the initialization process of C ++ objects. Some basic operating rules.

Constructor assignment

Usually, when we design a class, we will write the corresponding default constructor, copy constructor, copy assignment operator, and a deconstructor for this class. Even if we only write an empty class, the compiler will still declare a default constructor, copy constructor, copy assignment operator and deconstructor for it by default at compile time. If there are usage scenarios for them in the code, then this time the compiler will Create them.

class MyCppClass {}

Once we write a default constructor for a class, the compiler will not generate a default constructor for it by default, and it is the same for several other functions. For the constructor generated by the compiler by default, it will initialize each data member according to certain rules. Considering the importance of member initialization, you need to be rigorous and serious when writing your own constructor, especially in the case of class derivation and inheritance. For the application scenarios of copy constructor and assignment operator, I have to say a little more here, see the following code:

Copy code

#include <iostream>
 
using std::cout;
using std::endl;
 
class MyCppClass
{
public:
    MyCppClass()
    {
        std::cout <<"In Default Constructor!" <<std::endl;
    }
 
    MyCppClass(const MyCppClass& rhs)
    {
        std::cout <<"In Copy Constructor!" <<std::endl;
    }
 
    MyCppClass& operator= (const MyCppClass& rhs)
    {
        std::cout <<"In Copy Assignment Operator!" <<std::endl;
 
        return *this;
    }
};
 
int main()
{
    MyCppClass testClass1;                 // default constructor
    MyCppClass testClass2(testClass1);     // copy constructor
    testClass1 = testClass2;               // copy assignment operator
 
    MyCppClass testClass3 = testClass1;    // copy constructor
 
    return 0;
}

Copy code

Results of the:

 

It should be noted here that in general, we always think that the copy assignment operator is called where the '=' operator appears, but the above case is an exception. That is, when a new object is defined, even if the '=' operator is used at this time, it actually calls the initialization function copy constructor instead of calling the copy assignment operator to perform the assignment operation.

 

Why initialization list

An object includes two steps during initialization:

First, allocate memory to save this object;

Second, execute the constructor.

When the constructor is executed, if there is an initialization list, the initialization list is executed first, and then the function body of the constructor is executed. So why did you introduce the initialization list?

 

Compared with C, C ++ has changed from "function -oriented process-oriented " to "class -oriented object-oriented " in program organization. At the same time, class also serves as a composite data type, and the initialization list It is nothing more than to initialize some data. Considering this, it is also natural to speculate that the initialization list is related to the initialization of this type of data type.

After the introduction of the initialization list, there are two ways to initialize the data members of a class. The following is a comparison when the data member types of the class are built-in types and custom types. 

Copy code

// The data member type is the built-in type 
class MyCppClass 
{ 
public: 
    // The assignment operation initializes the member 
    MyCppClass 
    { 
        counter = 0; 
    } 
    
    // The initialization list performs member initialization 
    MyCppClass: counter (0) 
    { 
    } 

private: 
    int counter; 
}

Copy code

When the data member type of a class is a built-in type, the above two initialization methods have the same effect. When the data member type is also a class , the initialization process will be different, such as: 

Copy code

// The data member type is a custom type: a class 
class MyCppClass 
{ 
public: 
    // Assignment operation for member initialization 
    MyCppClass (string name) 
    { 
        counter = 0; 
        theName = name; 
    } 

    // Initialization list for member initialization 
    MyCppClass: counter ( 0), theName (name) 
    { 
    } 

private: 
    int counter; 
    string theName; 
}

Copy code

 

In the statement of theName = name in the constructor body , theName will first call the default constructor of the string to initialize, and then call the copy assignment opertor to copy the assignment. And for initialization list is , by direct initialize constructor Copy .

 

Obviously, the following code can be tested.

Copy code

#include <iostream>
#include <string>
 
class SubClass
{
public:
    SubClass()
    {
        std::cout <<" In SubClass Default Constructor!" <<std::endl;
    }
 
    SubClass(const SubClass& rhs)
    {
        std::cout <<" In SubClass Copy Constructor!" <<std::endl;
    }
 
    SubClass& operator= (const SubClass& rhs)
    {
        std::cout <<" In SubClass Copy Assignment Operator!" <<std::endl;
 
        return *this;
    }
};
 
class BaseClass
{
public:
    BaseClass(const SubClass &rhs)
    {
        counter = 0;
        theBrother = rhs;
        std::cout <<" In BaseClass Default Constructor!" <<std::endl;
    }
 
    BaseClass(const SubClass &rhs, int cnt):theBrother(rhs),counter(cnt)
    {
        std::cout <<" In BaseClass Default Constructor!" <<std::endl;
    }
 
    BaseClass(const BaseClass& rhs)
    {
        std::cout <<" In BaseClass Copy Constructor!" <<std::endl;
    }
 
    BaseClass& operator= (const BaseClass& rhs)
    {
        std::cout <<" In BaseClass Copy Assignment Operator!" <<std::endl;
 
        return *this;
    }
private:
    int counter;
    SubClass theBrother;
};
 
int main()
{
    SubClass subClass;
 
    std::cout <<"\nNo Member Initialization List: " <<std::endl;
    BaseClass BaseClass1(SubClass);
 
    std::cout <<"\nMember Initialization List: " <<std::endl;
    BaseClass BaseClass2(SubClass, 1);
 
    return 0;
}

Copy code

Results of the:

 

That is, when it comes to the initialization of custom types, using the initialization list to complete the initialization will have a better performance in terms of efficiency . This is also a highlight of the initialization list. Even for built-in types, in some cases, it is necessary to use the initialization list to complete the initialization work, such as const, references member variables. Here is a note with a very detailed description of the initialization list.

 

Several initialization nouns

When reading the Chinese version of "Accelerated C ++", I always encountered "default initialization", "implicit initialization" and "numerical initialization". At first, I had a lot of troubles in understanding these terms. So many nouns have been created, and for this reason, it took me less time to figure out the relationship between them.


In order to better understand them, first divide the data types in C ++. In C ++, data types can be roughly divided into two types: the first is built-in types, such as float, int, double, etc .; the second is a custom type, which is the class defined by our commonly used class, struct. When initializing these types of data, the difference manifests itself: for built-in types, you must initialize the display before use, and for custom types, the initialization responsibility falls on the constructor. 

int x = 0; // Display initialization x 
SubClass subClass; // Rely on SubClass's default constructor for initialization

The above term " default initialization " describes an initialization state when data of a built-in type or a custom type is not initialized for display. " Implicit initialization " describes the specific operation mode under this state. For example, for built-in types, the implicit initialization in the default initialization state is actually undefined, and the implicit type of the custom type Initialization depends on its constructor.

 

As mentioned earlier, C ++ does not guarantee the initialization of built-in types, but when the built-in type is a member of a class, the members of the built-in type will be actively initialized by the compiler under certain specific conditions. This process is also called Value initialization. In "Accelerated C ++", the following situations are listed:

  1. Objects are used to initialize a container element
  2. Add a new element to the mapping table, the object is a side effect of this add action
  3. Define a container of a specific length, the object is an element of the container

The test is as follows:

Copy code

#include <iostream> 
#include <vector> 
#include <map> 
#include <string> 
 
using std::cout; 
using std::endl; 
using std::vector; 
using std::map; 
using std::string; 
 
class NumbericInitTestClass 
{ 
public: 
    void PrintCounter() 
    { 
        cout <<"counter = " <<counter <<endl; 
    } 
private: 
    int counter; 
}; 
 
 
int main() 
{ 
    NumbericInitTestClass tnc; 
    tnc.PrintCounter(); 
 
    map<string, int> mapTest; 
    cout <<mapTest["me"] <<endl; 
 
    vector<NumbericInitTestClass> vecNumbericTestClass(1); 
    vecNumbericTestClass[0].PrintCounter(); 
 
    return 0; 
}

Copy code

For the built-in types that are not initialized, there is an undefined value of 2009095316, and for 2, 3 cases, they are all initialized to 0. For the first case, I have not thought of a suitable scenario.

 

Looking back, for some similar terms in the book, it is always far-fetched to think of ways to put them together :) 

 

Some rules

Here are a few basic rules about initialization , most of which are from "Effective C ++" :

 

1. Manually initialize built-in objects, because C ++ does not guarantee to initialize them.

2. The constructor is best to use member initialization list (member initialization list), rather than using assignment operations in the body of the constructor. The order of the member variables listed in the initial value column should be the same as the order in which they are declared in the class.

3. C ++ doesn't like destructors to spit out exceptions.

4. Do not call the virtual function during the constructor and destructor, because such calls never fall to the derived class.

5. The copying function should ensure that "all member variables in the object" and "all base class components" are copied.

Published 10 original articles · Like 11 · Visits 20,000+

Guess you like

Origin blog.csdn.net/u013323018/article/details/94916963