Почему инициализация структуры C ++ равной `= {0}` не устанавливает все ее члены равными 0? - PullRequest
4 голосов
/ 30 апреля 2020

После тонны тестирования и написания этого ответа (примечание: понижающий голос до него был ПЕРЕД моим полным переписыванием), я не могу понять, почему = {0} не устанавливает всех участников структуры в ноль!

Если вы сделаете это:

struct data_t
{
    int num1 = 100;
    int num2 = -100;
    int num3;
    int num4 = 150;
};

data_t d3 = {0};
printf("d3.num1 = %i\nd3.num2 = %i\nd3.num3 = %i\nd3.num4 = %i\n\n",
       d3.num1, d3.num2, d3.num3, d3.num4);

... вывод:

d3.num1 = 0
d3.num2 = -100
d3.num3 = 0
d3.num4 = 150

... хотя я ожидаемый вывод будет таким:

d3.num1 = 0
d3.num2 = 0
d3.num3 = 0
d3.num4 = 0

... что означает, что только первый элемент был установлен на ноль, а все остальные были установлены по умолчанию.

У меня всегда было впечатление, что инициализация структуры любым из этих трех способов приведет к ее нулевой инициализации, но, очевидно, я ошибаюсь!

  1. data_t d{}
  2. data_t d = {}
  3. data_t d = {0}

Мой ключ на вынос от Этот ответ , следовательно, таков:

Большой вынос здесь что НИЧЕГО из них: data_t d{}, data_t d = {} и data_t d = {0} фактически устанавливают все члены структуры в ноль!

  1. data_t d{} устанавливает все значения по умолчанию, определенные в структуре .
  2. И data_t d = {0} устанавливает только значение FIRST на ноль, а все остальные значения по умолчанию.

Итак, почему же при инициализации структуры C ++ = {0} все ее члены не устанавливаются в 0?

Обратите внимание, что мои ключевые выводы на самом деле противоречат этой довольно официальной документации, которую я использовал в течение многих лет (https://en.cppreference.com/w/cpp/language/zero_initialization), что говорит о том, что T t = {} ; и T {} ; оба являются нулевыми инициализаторами, тогда как на самом деле, согласно моим тестам и выносу выше, они НЕ.

Ссылки:

  1. Как инициализировать структуру в 0 в C ++
  2. Обновление: я тоже только что указал на эту ссылку: Что означает {0} при инициализации объекта?

Ответы [ 3 ]

7 голосов
/ 30 апреля 2020

Итак, почему инициализация структуры C ++ равной = {0} не устанавливает для всех ее членов значение 0?

Поскольку вы предоставляете только одно значение, в то время как класс имеет более одного члена.

Когда у вас есть T t{}; или T t = {}, то, что вы делаете, называется инициализация значения . При инициализации значения, если у объекта / члена нет конструктора по умолчанию или инициализатора члена по умолчанию, компилятор возвращается к нулю, инициализируя объект / член. Таким образом, с

data_t d{}

значение членов в порядке будет 100, -100, 0, 150, и что 0 для num3 происходит, потому что у него нет значения по умолчанию, и вы не указали значение в {}, поэтому компилятор возвращается к нулю, инициализируя num3. Это то же самое с data_t d = {}. С data_t d = {0} вы предоставляете первый элемент, поэтому num1 равен 0, но затем, как и первые два, все остальные члены инициализируются со значением по умолчанию, если они есть, или с нулевым, если они не инициализированы. , давая вам 0, -100, 0, 150 для значений элементов.

Это изменение произошло, когда C ++ 11 был выпущен и разрешен для инициализаторов элементов по умолчанию.


Если ваш data_t был определен как

typedef struct
{
    int num1;
    int num2;
    int num3;
    int num4;
} data_t;

, то data_t d{}, data_t d = {}, data_t d = {0} оставят вас с инициализированным нулем классом, так как нет инициализаторов членов по умолчанию и единственного значения вы предоставляете в braced-init-list (техническое имя для {...}) ноль, поэтому все члены становятся равными нулю.

5 голосов
/ 30 апреля 2020

data_t d3 = {0} - это список инициализации синтаксис, который с такими агрегатами, как data_t, выполняет агрегатная инициализация : предоставленное значение 0 используется для инициализации первого элемента, а остальные элементы инициализируются с использованием соответствующих им значений по умолчанию, и, если таковых не существует, инициализируется значением ( выделение шахты , отредактировано для C ++ 14 ):

Если количество предложений инициализатора равно меньше , то число членов или список инициализатора равно полностью пустой, остальные члены инициализируются их инициализаторами элементов по умолчанию , если это предусмотрено в определении класса и в противном случае с помощью пустых списков, в соответствии с обычными правилами для инициализации списка (которые выполняют значение-инициализацию для не-класса типы и неагрегированные классы с конструкторами по умолчанию, и agg Регуляция инициализации для агрегатов). Если элемент ссылочного типа является одним из этих оставшихся элементов, программа является некорректной.

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

Примечание: это не следует путать с по умолчанию -инициализация , которая вообще не инициализирует неклассные типы. data_t d3; будет default-initialization , и элемент num3 будет оставлен в неопределенном состоянии.

Важным моментом, за которым нужно следить, является ли объект или нет. инициализируется агрегат , потому что правила инициализации отличаются для агрегатов и классов с конструктором. В случае конструктора не принадлежащие классу члены без значения по умолчанию будут инициализированы по умолчанию (т.е. оставлены в неопределенном состоянии).

Некоторые примеры:

struct A { // an aggregate
    int num1 = 100;
    int num2 = -100;
    int num3;
};

struct B { // not an aggregate
    int num1 = 100;
    int num2 = -100;
    int num3;
    B() {}
    B(int) {}
};

int main() {
    A a1; // default-initialization: a1 is {100, -100, ???}
    A a2 = {}; // aggregate initialization: a2 is {100, -100, 0}
    A a3 = { 1 }; // aggregate initialization: a3 is {1, -100, 0}
    A a4 = { 1,2,3 }; // aggregate initialization: a4 is {1, 2, 3}
    B b1; // default-initialization: b1 is {100, -100, ???}
    B b2 = {}; // copy-list-initialization invoking B::B(): b2 is {100, -100, ???}
    B b3 = { 1 }; // copy-list-initialization invoking B::B(int): b3 is {100, -100, ???}
    B b4 = { 1,2,3 }; // error: no B constructor taking (int,int,int)
}

Обратите внимание также на то, что агрегат правила инициализации до C ++ 11. См., Например, этот связанный вопрос до C ++ 11: Что означает {0} при инициализации объекта?

2 голосов
/ 30 апреля 2020

Краткий ответ: потому что у вас есть инициализаторы в классе.

Более длинный ответ: потому что вы компилируете для C ++ 14 или выше, у вас есть инициализаторы в классе, и вы используете агрегатную инициализацию . Ссылка содержит объяснение:

Если количество предложений инициализатора меньше, чем количество элементов, или список инициализаторов полностью пуст, остальные элементы инициализируются инициализаторами их элементов по умолчанию, если они предоставлены в определение класса и иное (начиная с C ++ 14) с помощью пустых списков в соответствии с обычными правилами инициализации списков (которые выполняют инициализацию значений для типов не классов и неагрегированных классов с конструкторами по умолчанию, а также агрегатную инициализацию для агрегатов ).

Для инициализации нулями всех членов данных, предоставьте только объявления членов данных без инициализаторов в классе, а затем используйте синтаксис = {0} или ={}:

struct data_t
{
    int num1;
    int num2;
    int num3;
    int num4;
};

int main()
{
    data_t d3 = { 0 };
    printf("d3.num1 = %i\nd3.num2 = %i\nd3.num3 = %i\nd3.num4 = %i\n\n",
        d3.num1, d3.num2, d3.num3, d3.num4);
}

Теперь все ваши члены данных инициализируются как 0.

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