// Example program
#include <iostream>
#include <string>
class T{
public:
int x, y;
T(){
std::cout << "T() constr called..." << std::endl;
};
T(int x, int y):x(x),y(y){
std::cout << "T(x,y) constr called..." << std::endl;
}
void inspect(){
std::cout << "T.x: " << this->x << std::endl;
std::cout << "T.y: " << this->y << std::endl;
}
};
int main()
{
T t1(5,6);
t1.inspect();
std::cout << std::endl;
T t2 = {};
t2.inspect();
}
I am getting the following result:
T(x,y) constr called...
T.x: 5
T.y: 6
T() constr called...
T.x: 208787120
T.y: 31385
The members of t2
instance were not zero initialized (what I wanted to achieve). Do I understand it correctly, that if I have a constructor defined, it will not perform a zero initialization?
(I know how to achieve initialization to zero using explicit default values. The problem is why I am not able to do it using the init-list)
List initialization
Otherwise, if the braced-init-list is empty and T is a class type with a default constructor, value-initialization is performed.
Value-initialization
In all cases, if the empty pair of braces {} is used and T is an aggregate type, aggregate-initialization is performed instead of value-initialization.
Aggregate-initialization (it seems this is not my case and therefore it is not initializing members to zero)
An aggregate is one of the following types:
- class type (typically, struct or union), that has
- no user-declared constructors
What would be the simplest and less error-prone modification of legacy code, where I need to solve issues where some class members are used before they are initialized?
CodePudding user response:
The data members of t2
have garbage value. This is because they are of built-in type and you did not intialized them explicitly. The solution would be to:
Solution 1: Use constructor initializer list
T(): x(0), y(0){
std::cout << "T() constr called..." << std::endl;
};
Solution 2: Use in-class initializer
int x = 0, y = 0;
This is why it is advised that:
always initialize built-in type in block/local scope
If you use any of the above given solution, the output will be:
T(x,y) constr called...
T.x: 5
T.y: 6
T() constr called...
T.x: 0
T.y: 0
which is what you want and can be seen here and here.
Another solution would be to use delegating constructor (as suggested by @MarekR in the comment below) like:
T():T(0, 0)
{
std::cout << "T() constr called..." << std::endl;
}
CodePudding user response:
Do I understand it correctly, that if I have a constructor defined, it will not perform a zero initialization?
Yes.
Note that T
is not an aggregate because it contains user-provided constructors. As the effect of value initialization:
if T is a class type with no default constructor or with a user-provided or deleted default constructor, the object is default-initialized;
if T is a class type with a default constructor that is neither user-provided nor deleted (that is, it may be a class with an implicitly-defined or defaulted default constructor), the object is zero-initialized and then it is default-initialized if it has a non-trivial default constructor;
T
contains a user-provided default constructor, then #1 (but not #2 performing zero-initialization firstly) is applied.
In default initialization, the user-provided default constructor is used to initialize the object. The default constructor doesn't perform initialization on data members, they are initialized to indeterminate values.
CodePudding user response:
Zero initialization is a special case. The standard only guarantees member attributes to be default initialized. For class objects it indeed means that the default constructor will be called. But for basic type objects default initialization is just... no initialization at all. You have to explicitely ask for it if you need it:
T(): x(0), y(0) {
...