Preliminary analysis of C++ constructor

There are six default member functions in C++, namely constructor, destructor, copy constructor, assignment operator overloading, const member function, address taking and const address taking operator overloading. A default member function refers to a member function in a class that if the user does not explicitly implement it, the compiler will automatically generate a member function called a default member function, even if it is an empty class, it will be automatically implemented. The constructor is one of the most commonly used ones, so let’s start learning now (this section requires a bit of class foundation).

1. Basic concept: The constructor is a special member function with the same name as the class. It is automatically called by the compiler when creating a class type object to ensure that each data member has an appropriate initial value and is used throughout the entire life of the object. Only called once during the cycle.

Look at the example:

#include<iostream>
using namespace std;
class Date
{
public:
\t
Date(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
}
void Print()
{
cout << _year << "-" << _month << "-" << _day << endl;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
Date d1(2023, 11, 4);
d1.Print();
}

Running result:

From this we can see: through the constructor, we can quickly assign values to member variables when creating an object.

2. Characteristics:

The constructor is a special member function. It should be noted that although the name of the constructor is construction, the main task of the constructor is not to open space to create objects, but to initialize the objects. Its characteristics are as follows

1. The function name is the same as the class name. 2. No return value. 3. The compiler automatically calls the corresponding constructor when the object is instantiated. 4. Constructors can be overloaded.

Among them, we mainly explain the fourth point, look at the example:

 class Date
 {
  public:
      // 1. Parameterless constructor
      Date()
     {
        _year = 1900;
_month = 1;
_day = 1;
     }
  
      // 2. Constructor with parameters
      Date(int year, int month, int day)
     {
          _year = year;
          _month = month;
          _day = day;
     }
  private:
      int _year;
      int _month;
      int _day;
 };
  
  void TestDate()
 {
      Date d1; // Call the parameterless constructor
      Date d2(2015, 1, 1); // Call the constructor with parameters
 }

When a constructor is overloaded, the compiler will select an appropriate constructor to assign a value to the object based on the type, number, and order of parameters passed in. If the appropriate constructor cannot be found, the compiler will report an error. There is another detail in the above code. When calling the parameterless constructor, it must be written as Date d1, not Date d1(). The reason is that writing Date d1() will cause the compiler to misunderstand that you are declaring a function with a return type of Data, a function name of d1, and empty formal parameters, causing misunderstanding.

Special note: Once the user explicitly defines any constructor, the compiler will no longer generate a default constructor.

3. Default constructor

What function will the constructor automatically generated by the compiler accomplish? Let’s take a look together.

#include<iostream>
// std is the namespace name of the C++ standard library. C++ puts all the definitions and implementations of the standard library into this namespace.
using namespace std;
class Date
{
public:
//If you don’t write a constructor, the compiler will generate a default constructor

void Print()
{
cout << _year << "-" << _month << "-" << _day << endl;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
Date d1;
d1.Print();
}

operation result:

You can see that the constructor generated by the compiler just assigns the member variables of d1 to random values. It seems that the default constructor has no role here, so let’s look at an example:

#include<iostream>
// std is the namespace name of the C++ standard library. C++ puts all the definitions and implementations of the standard library into this namespace.
using namespace std;
class Time
{
public:
Time()
{
cout << "Time()" << endl;
_hour = 0;
_minute = 0;
_second = 0;
}
Time(int hour,int minute,int second)
{
_hour = hour;
_minute = minute;
_second = second;
}
int _hour;
int _minute;
int _second;
};
class Date
{
public:
void Print()
{
cout << _year << "-" << _month << "-" << _day <<" " << _t._hour<<" " << _t._minute<<" " << _t._second;
}
private:
//Basic type (built-in type)
int _year;
int _month;
int _day;
// Custom type
Time_t;

};


int main()
{
Date d1;
d1.Print();
return 0;
}

In the above code: We have added a new Time class and a new Time type variable in the Date class. We do not write the constructor of the Date class and use the default constructor generated by the compiler. Let us look at the different members of d1. variable value

Running result:

It can be seen that the member variables of type int in Data are still random values, while the member variables of type Time call its no-argument constructor for assignment. From this, we can draw the following conclusions:

1. For member variables of basic types (also called built-in types) (that is, types defined by the compiler such as int, double, float, etc.), when the default constructor generated by the compiler is called, they will generally be assigned random values (but There are also some compilers that assign specific values based on the type, such as assigning the value of the int type to 0, but most compilers assign random values).

2. For member variables of a custom type (such as Time above), when the compiler of the class in which this type is located is called to generate a default constructor , the function inside the custom member variable will be automatically called. Default construction (there are three types: no-argument construction, default construction automatically generated by the compiler, and all-default construction) (it can be thought that because we do not pass in parameters to the constructor of this member variable, the call is not used Parameter-passing constructor, i.e. default construction).

4. Initialization list:

private:
//Basic type (built-in type)
int _year;
int _month;
int _day;
// Custom type
Time_t;

In the above example, there is a piece of code, which is a declaration of member variables, but variables modified by references and const must be assigned a value and initialized when they are declared. At this time, we must use the initialization list. (Note that the constructor only assigns values to member variables, because initialization can only be initialized once, while the constructor body can assign values multiple times)

Generally speaking, there are three types of member variables that must be written into the initialization list: 1. Reference member variables 2. Const member variables 3. Custom type members without a default constructor (The default cannot be called at this time Constructor, can only be put into the initialization list to pass parameters and call non-default constructor)

Usage format: starts with a colon, followed by a comma-separated list of data members, each “member variable” followed by an initial value or expression in parentheses.

Look at the example:

class Date
{
public:
Date(int year, int month, int day)

     : _year(year)
     , _month(month)
     , _day(day)
{

}
private:
int _year;
int _month;
int _day;
};

We may be curious about how the compiler will handle other variables that are not put into the initialization list, look at the example:

#include<iostream>
using namespace std;
class Time
{
public:
Time()
{
cout << "Time()" << endl;
_hour = 0;
_minute = 0;
_second = 0;
}
Time(int hour,int minute,int second)
{
_hour = hour;
_minute = minute;
_second = second;
}
int _hour;
int _minute;
int _second;
};
class Date
{
public:
void Print()
{
cout << _year << "-" << _month << "-" << _day <<" " << _t._hour<<" " << _t._minute<<" " << _t._second;
}
Date(int year)
: _year(year)
{

}
\t
private:
\t\t
int _year;
int _month;
int _day;
Time_t;
\t\t

};


int main()
{
Date d1(1);
d1.Print();
cout << endl;

}

Running result:

You can see that when other member variables are not in the custom list of the custom constructor, random values are generally generated for built-in types, and the default constructor is called for custom types (if not, an error will be reported). This is the same as the constructor automatically generated by the system. The functions are very similar.

General types can be placed in the initialization list or in the function body of the constructor, but three special types must be placed in the initialization list. like

class A
{
public:
 A(int a)
 :_a(a)
 {}
private:
 int _a;
};
class B
{
public:
 B(int a, int ref)
 :_aobj(a)
 ,_ref(ref)
 ,_n(10)
 {}
private:
 A _aobj; // No default constructor
 int & amp; _ref; // reference
 const int _n; // const
};

In addition, please note: 1. Each member variable can only appear once in the initialization list (initialization can only be initialized once)

2. The order in which member variables are declared in a class is the order in which they are initialized in the initialization list, regardless of their order in the initialization list.

Take a look at the following questions:

class A
{
public:
    A(int a)
       :_a1(a)
       ,_a2(_a1)
   {}
    
    void Print() {
        cout<<_a1<<" "<<_a2<<endl;
   }
private:
    int _a2;
    int _a1;
};
int main() {
    A aa(1);
    aa.Print();
}

There are four options: A. Output 1 1 B. Program crash C. Fail to compile D. Output 1 random value

Correct answer: D, because the initialization order is in the order of declaration of member variables and has nothing to do with the order in the initialization list. First, use _a1 that has not been assigned (the variable that has not been assigned is a random value) to assign a value to _a2, and then use The passed in 1 assigns a value to _a1. Therefore the answer is D.

Okay, that’s it for explaining the constructor. If you have any mistakes or confusion, you can point them out in the comment area. Thank you for reading this.