Операция присваивания в списках инициализаторов элементов - PullRequest
1 голос
/ 07 марта 2019

У меня есть следующий класс стека.

class Stack{
public:
    int size;
    int* x;
    Stack() : size(10), x(new int[10]) {}
    Stack(const Stack& s) : x(new int[size=s.size]) {}

};

Обратите внимание на назначение в конструкторе копирования.Код работает, хорошо компилируется и компилятор (gcc) не жалуется даже с флагами -Wall -Wextra.Компилятор автоматически переписывает компилятор на это?

Stack(const Stack& s) : size(s.size), x(new int[size]) {}

Или есть какая-то другая магия?Я заметил, что когда я меняю порядок определения, компилятор жалуется на инициализацию не по порядку.Поэтому я предполагаю, что это тот случай, о котором я упоминал.Я ничего не нашел в документации, и вывод ASM мне тоже не помог.

Ответы [ 4 ]

4 голосов
/ 07 марта 2019

Компилятор автоматически переписывает компилятор в это?

Stack(const Stack& s) : size(s.size), x(new int[size]) {}

номер

Stack(const Stack& s) : x(new int[size=s.size]) {}

может быть как

Stack(const Stack& s) : size(), x(new int[size=s.size]) {}

но на самом деле это не так, поскольку на самом деле запись size() будет иметь значение для его инициализации, то есть инициализируется с нуля, но поскольку компилятор инициализирует инициализацию, инициализация по умолчанию [1] происходит, то есть не инициализируется. Затем вы присваиваете ему значение в инициализации x. Это «безопасно», то есть в данном случае это работает, но я бы не рекомендовал это.

Stack(const Stack& s) : size(s.size), x(new int[s.size]) {}

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

2 голосов
/ 07 марта 2019

Члены класса всегда инициализируются в порядке объявления, независимо от того, какой порядок вы указали в списке инициализации членов.И если вы их опустите, они будут инициализированы по умолчанию.Таким образом:

Stack(const Stack& s) : x(new int[size=s.size]) {}

Означает, что size по умолчанию инициализируется первым.Это оставляет его с неопределенным значением (поскольку фундаментальные типы должны быть инициализированы по умолчанию).Затем инициализатор для x оценивается.Часть оценки new int[size=s.size] включает выражение присваивания size=s.size, которое модифицирует size как побочный эффект.Таким образом, ваш код здесь верен, несмотря на то, что он может вызвать удивление.

Когда вы меняете порядок членов, тогда присваивание происходит за до инициализации size.Это оставляет ваш код открытым для неопределенного поведения.

0 голосов
/ 07 марта 2019

Нет, он переписывает это так:

class Stack{
public:
    int size;
    int* x;
    Stack() : size(10), x(new int[10]) {}
    Stack(const Stack& s) :size(), x(new int[size=s.size]) {}
};

size() будет конструктором по умолчанию для объекта size, но int s не имеет такового, так что это просто пустойзаявление и size остается неинициализированным. Или так?

Я бы сказал, что этот код может генерировать неопределенное поведение.Переменные-члены инициализируются в том порядке, в котором они объявлены в соответствии с 10.9.2 Инициализация баз и элементов .Но порядок вычисления выражений, используемых для инициализации, не определяется.Таким образом, size=s.size можно вызывать до или после size().Для типов классов это было бы проблемой, но я не уверен, гарантированно ли size() будет no-op и может ли компилятор решить инициализировать переменную, например, 0.

0 голосов
/ 07 марта 2019

Со страницы Cppreference.com на конструкторы :

Имена, которые появляются в выражении-списке или скобке-init-списке, оцениваются в области действия конструктора:

Так что да, size здесь относится к this->size в области видимости конструктора, а присваивание size=s.size является допустимым выражением.

Само собой разумеется, что вы должныне ожидайте, что это пройдет проверку кода:)

...