Почему при инициализации в скобках переменные заполняются мусором? - PullRequest
7 голосов
/ 27 июня 2019

Я пришел к выводу, что переменным присваиваются значения по умолчанию при использовании инициализации фигурной скобки. Но я был неправ.

В следующем примере:

#include <string>
#include <iostream>

#include <stdint.h>


class A
{
public:
    A() {}
    ~A(){}

    int var1;
    int32_t var2;
    int64_t var3;
    std::string var4;
    double var5;
    float var6;

    std::string info() const {
        return "var1=" + std::to_string(var1) + " " +
               "var2=" + std::to_string(var2) + " " +
               "var3=" + std::to_string(var3) + " " +
               "var4=" + var4 + " " +
               "var5=" + std::to_string(var5) + " " +
               "var6=" + std::to_string(var6) + " " +
               "\n"
               ;
    }
};

int main()
{
    A a;
    std::cout << "Before assigning variables: " << a.info();

    a.var1 = 1;
    a.var2 = 2;
    a.var3 = 3;
    a.var4 = "4";
    a.var5 = 5;
    a.var6 = 6;
    std::cout << "After assigning variables: " << a.info();

    a = {};
    std::cout << "After brace init assignment: " << a.info();
}

Вот результат:

Before assigning variables: var1=0 var2=0 var3=4198240 var4= var5=0.000000 var6=0.000000 
After assigning variables: var1=1 var2=2 var3=3 var4=4 var5=5.000000 var6=6.000000 
After brace init assignment: var1=2114725200 var2=32766 var3=4199416 var4= var5=0.000000 var6=0.000000

Чтобы исправить это:

  1. Если я избавлюсь от конструктора по умолчанию - проблема исчезнет.
  2. Если переменная-член класса инициализируется скобками, ей будет присвоено значение 0 или значение по умолчанию. E.g.:

    class A
    {
    public:
        A() {}
        ~A(){}
    
        int var1{};
        int32_t var2{};
        int64_t var3{};
        std::string var4{};
        double var5{};
        float var6{};
    };
    

Может кто-нибудь объяснить, почему это происходит? Что мне здесь не хватает?

Ответы [ 2 ]

8 голосов
/ 27 июня 2019

a = {}; - это присвоение, a назначается из временного объекта, построенного из {}.Неявно сгенерированное присваивание выполнит посимвольное присваивание для всех элементов данных, тогда будет смысл, как временный объект инициализируется из {}.

Это copy-list-инициализация , в качестве эффекта выполняется инициализация значения .

В противном случае, если список фигурных скобок пуст, а T является типом класса сконструктор по умолчанию, выполняется инициализация значения .

В качестве эффекта инициализация значения ,

1), еслиT - это тип класса без конструктора по умолчанию или с предоставленным пользователем или удаленным конструктором по умолчанию, объект инициализированный по умолчанию ;

A имеет предоставленный пользователемконструктор по умолчанию и как результат инициализация по умолчанию , этот конструктор по умолчанию используется для инициализации временного объекта.Тело предоставленного пользователем конструктора по умолчанию пустое, тогда для временного объекта var4 будет инициализирован по умолчанию конструктором по умолчанию std::string, все остальные члены данных со встроенным типом будут иметьнеопределенные значения.

  1. Если я избавлюсь от конструктора по умолчанию - проблема исчезнет.

Тогда поведение значение-инициализация изменится на

(выделено мной)

2), если T - это тип класса с конструктором по умолчанию, который не предоставлен и не удален пользователем (то есть itможет быть классом с неявно определенным или дефолтным конструктором по умолчанию ), , объект инициализируется нулями , а затем инициализируется по умолчанию у него нетривиальный конструктор по умолчанию;

Обратите внимание на разницу, временный объект сначала будет инициализирован нулями .Затем все элементы данных со встроенным типом инициализируются на 0 (var4 по-прежнему инициализируется по умолчанию ).

Если переменная-член класса инициализируется скобками, то ей будет присвоено значение 0 или значение по умолчанию.

Так работает список инициализаторов по умолчанию .

Посредством инициализатора элемента по умолчанию, который является просто инициализатором скобок или равенства, включенным в объявление элемента, который используется, если элемент пропущен в списке инициализатора элемента

Затем все члены данных инициализируются указанным инициализатором;в вашем примере все они инициализированы значением , в результате var4 будет инициализирован по умолчанию , остальные члены инициализированы нулями до 0.

4 голосов
/ 27 июня 2019
a = {};

Эта строка не означает, что все переменные внутри класса получают инициализатор {}. Вместо этого он вызывает (не определенный, поэтому автоматически сгенерированный) оператор присваивания (или перемещения), который выполняет поверхностное копирование / перемещение из объекта, созданного с помощью {} (т.е. с неинициализированными переменными), к имеющемуся у вас объекту.

var4 кажется очищенным, но на самом деле скопировано / перемещено из нового объекта var4, и поскольку std :: string имеет конструктор по умолчанию, он пуст.

Простое решение, позволяющее избежать подобных вещей, - инициализировать переменные вне класса внутри класса, скажем

class A
{
    int var = 0;
    ...

};
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...