Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ object initialization with copy-list-initializer

// 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?

like image 421
Khamyl Avatar asked Jan 25 '23 05:01

Khamyl


2 Answers

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:

  1. if T is a class type with no default constructor or with a user-provided or deleted default constructor, the object is default-initialized;

  2. 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.

like image 77
songyuanyao Avatar answered Jan 30 '23 08:01

songyuanyao


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;
}
like image 20
Anoop Rana Avatar answered Jan 30 '23 08:01

Anoop Rana