Answer a question from someone else in the forum
There is a passage in C++ Primer when talking about constructor initialization list:
Whether initializing members in the constructor initialization list or assigning values to them in the constructor body, the end result is the same. The difference is that the data members are initialized using the version of the constructor initialization list, and the constructor version of the initialization list does not define the assignment of the data members in the constructor body.
What does the meaning of initializing data members and assigning data members here? What's the difference?
I know that there are differences when data members have default constructors, but what about other types of members? Is there any difference between initialization and assignment of other types of members?
=========================================================================
That's what it means:
First classify data members by type
1. Built-in data type, compound type (pointer, reference)
2. User-defined type (class type)
Sub-situation description:
For type 1, it is performed in the member initialization list and the constructor body, and the performance and results are the same
For Type 2, the results are the same, but there are big differences in performance.
Because the data member object of the class type has been constructed when entering the function body, that is, the work of constructing the object is carried out at the member initialization list. This is to call a constructor. After entering the function body, it is done to assign the already constructed class object and call a copy assignment operator to complete it (if it is not provided, the default assignment behavior provided by the compiler is used to provide a member)
Give an example
class A;
class B
{public:
B(){a = 3;}
private:
A a;
}
class A
{public:
A(){}
A(int){value = 3;}
int value;
}
As above, we make the value of object a value of 3, call a constructor of A + a default copy assignment to achieve the goal.
B::B():a(3){}
Like this, only one constructor is called to achieve the required object, so the performance is good
Reprinted by someone else
My question is about initializing C++ class members. I've seen many such codes (including in your columns, too):
CSomeClass::CSomeClass()
{
x=0;
y=1;
}
And somewhere else it is written as follows:
CSomeClass::CSomeClass() : x(0), y(1)
{
}
Some of my programmers and friends said that the second method is better, but they don’t know why this is the case. Can you tell me the difference between the two class member initialization methods?
answer
Technically, your programmer friend is right, but in most cases, there is actually no difference between the two. There are two reasons why we choose the second syntax, which is called a member initialization list: one is necessary, and the other is just for efficiency reasons.
Let's first look at the first reason - necessity. Imagine you have a class member, which is itself a class or structure, and only has a constructor with one parameter.
class CMember {
public:
CMember(int x) { ... }
};
Because Cmember has an explicitly declared constructor, the compiler does not generate a default constructor (without parameters), so an instance of Cmember cannot be created 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 using member
CMyClass::CMyClass() : m_member(2)
{
•••
}
There is no other way to pass parameters to m_member, the same is true if the member is a constant object or a reference. According to C++ rules, constant objects and references cannot be assigned, they can only be initialized.
The second reason is for efficiency reasons when the member class has a default constructor and an assignment operator. MFC's Cstring provides a perfect example. Suppose you have a class CmyClass with a member m_str of type Cstring, and you want to initialize it to "yada yada." You have two options:
CMyClass::CMyClass() {
// Use the assignment operator
// CString::operator=(LPCTSTR);
m_str = _T("yada yada");
}
//Use class member list
// and constructor CString::CString(LPCTSTR)
CMyClass::CMyClass() : m_str(_T("yada yada"))
{
}
Is there any difference between them? Yes. The compiler always ensures that all member objects are initialized before the constructor body is executed, so the code compiled in the first example will call 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 this function. The result is that in the first example, two Cstring functions (constructor and assignment operator) are called, while in the second example, only one function is called. In the Cstring example this doesn't matter, because the default constructor is inline, and Cstring just allocates memory for the string when needed (i.e., when you actually assign a value). However, in general, repeated function calls are a waste of resources, especially when constructors and assignment operators allocate memory. In some large classes, you may have a constructor and an assignment operator that both have to call the same Init function responsible for allocating a large amount of memory space. In this case, you have to use the initialization list to avoid not allocating memory twice. Under internal types such as ints or longs or other types without constructors, there is no performance difference between the initialization list and assignment of values within the constructor body. No matter which method is used, there will only be one assignment. Some programmers say you should always use initialization lists to maintain good habits, but I never found any difficulty in converting between the two methods as needed. In programming style, I tend to use assignments in the body because there is more space to format and add comments, you can write a statement like this: x=y=z=0;
or memset(this,0,sizeof(this));
Note that the second fragment is absolutely non-object-oriented.
When I consider the issue of initializing lists, there is a weird feature I should warn you that it is about C++ initialization class members, which are initialized in the order declared, not in the order that appears in the initialization 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 above code will first do m_y=I, then m_x=m_y, and finally they 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 design illustrates this, but this bug will appear more naturally. There are two ways to avoid it, one is to always declare members in the order you want them to be initialized, and the second is to always list these members in the order they are declared if you decide to use the initialization list. This will help eliminate confusion.