The differences between the member initialization list and the constructor body are parsed in detail

  • 2020-04-02 01:42:03
  • OfStack

Answer a question in the forum

In C++ Primer, when we talk about initializing a list of constructors, we have this quote:
The end result is the same whether the members are initialized in the constructor initialization list or assigned values in the constructor body. The difference is that the version that initializes the list with a constructor initializes the data members, and the constructor version that does not define the initializer list assigns values to the data members in the constructor body.

What is the meaning of initializing data members and assigning values to them? What's the difference?

I know it's different when the data member has a default constructor, but what about other types of members? Is there any difference between initialization and assignment of members of other types?
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
It means:
Start by categorizing the data members by type
1. Built-in data types, composite types (Pointers, references)
2. User defined type (class type)

Case studies:
For type 1, both in the member initialization list and in the constructor body, the performance and results are the same
For type 2, the results are the same, but there is a big difference in performance

Because the data members of the class type object into the body of the function is constructed, which means the member object initialization list for construction work, this is a call to a constructor, after entering the function body, the is the value assigned to class object is constructed, and invoke a copy assignment operator to complete (if it does not provide members use the default provided by the compiler according to assignment behavior)

Let me give you an example
Class A;
Class B
{public:
(B) {a = 3; }
Private:
A, A.
}

Class A,
{public:
A () {}
A (int) {value = 3; }
The int value.
}

Like above, we set the value of the a object to 3 by calling a constructor of a + a default copy assignment
B: : B() : a (3) {}
Like this, it only takes one constructor call to get the required object, so the performance is good

Reprint another article

My question is about initializing C++ class members. I've seen a lot of this code (including in your column) :


CSomeClass::CSomeClass()
{
    x=0;
    y=1;
}

Somewhere else it would look like this:

CSomeClass::CSomeClass() : x(0), y(1)
{
}

Some of my programmer friends say the second way is better, but they don't know why. Can you tell me the difference between the two kinds of member initialization methods?

answer

Technically, your programmer friends are right, but in most cases there is virtually no difference. There are two reasons for choosing the second syntax, which is called a member initialization list: one is necessary, and the other is simply for efficiency.

Let's start with the first reason -- necessity. Imagine you have a class member, which is itself a class or structure, and only one constructor with one argument.


class CMember {
public:
    CMember(int x) { ... }
};

Because Cmember has an explicitly declared constructor, the compiler does not produce a default constructor (with no arguments), so you cannot create an instance of Cmember without an integer.

CMember* PM = new CMember;               / / Error!!!!!
CMember* PM = new CMember(2);         / / OK

If Cmember is a member of another class, how do you initialize it? You must initialize the list with members.


class CMyClass {
    CMember m_member;
public:
    CMyClass();
};
//The list must be initialized with members
CMyClass::CMyClass() : m_member(2)
{
•••
}

There is no other way to pass an argument to m_member, if the member is a constant object or a reference. By C++ rules, constant objects and references cannot be assigned, they can only be initialized.

The second reason is for efficiency when a member class has a default constructor and an assignment operator. MFC's Cstring provides a perfect example. Suppose you have a class CmyClass that has a member m_str of type Cstring, and you want to initialize it as "yada yada." You have two choices:


CMyClass::CMyClass() {
    //Use the assignment operator
    // CString::operator=(LPCTSTR);
    m_str = _T("yada yada");
}
//Use a list of class members
// and constructor CString::CString(LPCTSTR)
CMyClass::CMyClass() : m_str(_T("yada yada"))
{
}

Is there any difference between them? B: yes. The compiler always ensures that all member objects are initialized before the constructor body is executed, so the compiled code in the first example calls CString:: CString to initialize m_str, which is done before the control reaches the assignment statement. In the second example the compiler generates a call to CString:: CString(LPCTSTR) and passes "yada yada" to the function. The result is that two Cstring functions (the constructor and the assignment operator) are called in the first example, and only one function is called in the second. In the case of the Cstring, this doesn't matter because the default constructor is inline, and the Cstring just allocates memory to the string as needed (that is, when you actually assign). In general, however, repeated function calls are a waste of resources, especially when constructors and assignment operators allocate memory. In some large classes, you might have a constructor and an assignment operator that both call the same Init function that is responsible for allocating a lot of memory. In this case, you must use the initialization list to avoid not allocating memory twice. There is no difference in performance between initializing the list and assigning values in the constructor for internal types such as ints or longs or any other type that does not have a construction function. Either way, there will only be one assignment. Some programmers say that you should always use initialization lists to keep in good habit, but I've never found it difficult to switch between the two methods as needed. In terms of programming style, I prefer to use assignments in the body, because there is more space for formatting and adding comments. You can write statements like this: x=y=z=0;

Or memset (this, 0, sizeof (this));

Note that the second fragment is definitely non-object oriented.

One strange feature I should warn you about when I think about initializing lists is that it's about C++ initializing class members in the declared order, not in the order that they appear in the initializing list.


class CMyClass {
    CMyClass(int x, int y);
    int m_x;
    int m_y;
};
CMyClass::CMyClass(int i) : m_y(i), m_x(m_y)
{
}

You might think that the code above would first do m_y=I, then m_x=m_y, and then they would have the same value. But the compiler initializes m_x and then m_y, because they are declared in this order. The result is that m_x will have an unpredictable value. My example is designed to illustrate this, but this bug will appear more naturally. There are two ways to avoid it, one is to always declare the members in the order you want them to be initialized, and the other is to always list the members in the order they are declared if you decide to use an initialization list. This will help to eliminate confusion.


Related articles: