The class generated by this template associates a name with a pointer to a T-type object.
Template <class T>
Class namedptr {
Public:
Namedptr (const string & initname, T * initptr );
...
PRIVATE:
String name;
T * PTR;
};
(Because objects with pointer members may cause pointer confusion during copying and assigning values (see article 11), namedptr must also implement these functions (see Article 2 ))
When writing a namedptr constructor, you must pass the parameter value to the corresponding data member. There are two methods to achieve this. The first method is to use the members to initialize the list:
Template <class T>
Namedptr <t>: namedptr (const string & initname, T * initptr)
: Name (initname), PTR (initptr)
{}
The second method is to assign values in the constructor body:
Template <class T>
Namedptr <t>: namedptr (const string & initname, T * initptr)
{
Name = initname;
PTR = initptr;
}
The two methods are significantly different.
From the perspective of actual applications, Initialization is required in some cases. In particular, const and referenced data members can only be initialized and cannot be assigned values. Therefore, if you want the namedptr <t> object to be unable to change its name or pointer member, you must follow the recommendation in Clause 21 to declare the member as const:
Template <class T>
Class namedptr {
Public:
Namedptr (const string & initname, T * initptr );
...
PRIVATE:
Const string name;
T * const PTR;
};
The definition of this class requires a member to initialize the list, because the const member can only be initialized and cannot be assigned a value.
If the namedptr <t> object contains an existing name reference, the situation is very different. However, you still need to initialize the reference in the initialization list of the constructor. You can also declare the const and reference for the name at the same time, so that a member of the name can be modified outside the class but is read-only inside the class.
Template <class T>
Class namedptr {
Public:
Namedptr (const string & initname, T * initptr );
...
PRIVATE:
Const string & name; // The list must be initialized by members.
// Initialize
T * const PTR; // The list must be initialized by members.
// Initialize
};
However, the previous class template does not contain const and reference members. Even so, it is better to initialize the list with members than to assign values in the constructor. The reason for this is efficiency. When the list is initialized using members, only one string member function is called. In the constructor, two values are called. To understand why, see what happened when declaring the namedptr <t> object.
The object can be created in two steps:
1. Data member initialization. (See clause 13)
2. Execute the actions in the called constructor body.
(For objects with a base class, the initialization of the base class members and the execution of the constructor bodies take place before the member initialization of the derived class and the execution of the constructor bodies)
For the namedptr class, this means that the constructor of the string object name is always inProgramThe constructor has been called before being executed to the namedptr. The question is: which constructor of string will be called?
This depends on the list of namedptr class member initialization. If no initialization parameter is specified for name, the default string constructor is called. When a value is assigned to the name in the namedptr constructor, operator = function is called for the name. So there are two calls to the string member function in total: one is the default constructor, and the other is the value assignment.
On the contrary, if you use a member initialization list to specify that the name must be initialized using initname, the name will be initialized by copying the constructor at the cost of one function call.
Even for a simple string type, unnecessary function calls can cause a high price. As classes become larger and more complex, their constructors become more and more complex, and the cost of object creation increases. The habit of using the member initialization list as much as possible can not only meet the const and reference member initialization requirements, but also greatly reduce the chance of inefficient data member initialization.
In other words, initialization through the member initialization list is always legal, and the efficiency is never lower than assigning values in the constructor body. It will only be more efficient. In addition, it simplifies the maintenance of the class (see the M32 clause), because if a data member is modified to a data type that must be initialized by the Member, then, no need to change anything.
However, in one case, it is more reasonable to assign values to data members of a class than to use initialization. This is when a large number of fixed data members need to be initialized in the same way in each constructor. For example, there is a class to illustrate this situation:
Class manydatambrs {
Public:
// Default constructor
Manydatambrs ();
// Copy the constructor
Manydatambrs (const manydatambrs & X );
PRIVATE:
Int a, B, c, d, e, f, g, h;
Double I, J, K, L, M;
};
If you want to initialize all the int values to 1 and all the double values to 0, you need to write the initialization list as follows:
Manydatambrs: manydatambrs ()
: A (1), B (1), C (1), D (1), E (1), F (1), G (1 ), H (1), I (0 ),
J (0), K (0), L (0), m (0)
{...}
Manydatambrs: manydatambrs (const manydatambrs & X)
: A (1), B (1), C (1), D (1), E (1), F (1), G (1 ), H (1), I (0 ),
J (0), K (0), L (0), m (0)
{...}
This is not just an annoying and boring job, but it is easy to make mistakes in the short term and difficult to maintain in the long term.
However, you can use a fixed data type (non-const, non-reference) object to initialize and assign values without any operation, the member initialization list is safely replaced by a call to a common initialization function.
Class manydatambrs {
Public:
// Default constructor
Manydatambrs ();
// Copy the constructor
Manydatambrs (const manydatambrs & X );
PRIVATE:
Int a, B, c, d, e, f, g, h;
Double I, J, K, L, M;
Void Init (); // used to initialize data members
};
Void manydatambrs: Init ()
{
A = B = c = d = E = f = G = H = 1;
I = J = k = L = m = 0;
}
Manydatambrs: manydatambrs ()
{
Init ();
...
}
Manydatambrs: manydatambrs (const manydatambrs & X)
{
Init ();
...
}
Because the initialization function is only an implementation detail of the class, you must declare it as a private member.
Note that static class members will never be initialized in the class constructor. Static members are initialized only once during the program running. Therefore, it makes no sense to initialize the class objects whenever they are created. At least this will affect the efficiency: Since it is "initialized", why do we need to perform multiple times? In addition, the initialization of static class members is very different from that of non-static class members. This is specifically described in the m47 clause.