Подтвердить что ты не робот

Почему я должен инициализировать переменные-члены в том порядке, в котором они объявлены?

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

Пример:

class Test {
    int a;
    int b;

public:
    Test() : b(1), a(2) {
    }
};

int main() {
    Test test;
    return 0;
}

Тогда, если я скомпилирую его с помощью -Werror -Wall:

$ g++ -Werror -Wall test.cpp
test.cpp: In constructor ‘Test::Test()’:
test.cpp:3:9: error: ‘Test::b’ will be initialized after [-Werror=reorder]
test.cpp:2:9: error:   ‘int Test::a’ [-Werror=reorder]
test.cpp:6:5: error:   when initialized here [-Werror=reorder]
cc1plus: all warnings being treated as errors

Я понимаю, что -Wall явно запрашивает GCC, чтобы он переходил сверху с предупреждениями, но я предполагаю, что есть причина для всех. Итак, как может быть порядок инициализации переменных-членов?

4b9b3361

Ответ 1

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

Это должно помочь предотвратить ошибки, когда инициализация b зависит от a или наоборот.

Причина этого упорядочения состоит в том, что существует только один деструктор, и ему нужно выбрать "обратный порядок" для уничтожения члена класса. В этом случае самым простым решением было использование порядка объявления внутри класса, чтобы убедиться, что атрибуты всегда были уничтожены в правильном обратном порядке.

Ответ 2

Зачем мне инициализировать переменные-члены в том порядке, в котором они объявлены?

Элементы будут инициализироваться в том же порядке, в котором они объявлены, хотите ли вы этого или нет. Предупреждение сообщает вам, что заказ, который вы запрашиваете, отличается от фактического порядка выполнения инициализации.

Ответ 3

Вы не должны, потому что это уменьшает читаемость и потенциально вводит в заблуждение.

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

Test() : b(1), a(b) {}

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

Ответ 4

Собственно, компилятор всегда инициализирует переменные в порядке объявления, даже если вы пишете инициализаторы в другом порядке. Поэтому, если вы не записываете инициализации в порядке объявления, порядок ваших инициализаторов не соответствует порядку инициализации, что может привести к тонким ошибкам, если инициализация зависит друг от друга.

Например, рассмотрим код

Test(): b(42), a(b) {}

Это ошибка, потому что a инициализируется до b, но выглядит нормально. Если вы пишете его в порядке объявления (который является порядком инициализации), ошибка становится очевидной:

Test(): a(b), b(42) {}

Обратите внимание, что ошибка также может быть более тонкой, чем та, например, представить a и b типы классов, которые выводят что-то в их конструкторе; то с "неправильным" порядком вы думаете, что вывод b должен появиться перед a, когда в реальности произойдет обратное. Если вывод a, который появляется первым, приведет к недопустимому файлу, что также приведет к ошибке, но компилятор не сможет заметить проблему, если конструкторы находятся в другой единицы перевода (кроме того, что компилятор не может знать, является ли переупорядочение или не является ошибкой). Поэтому разумно, что компилятор просто предупреждает о каждом экземпляре несогласованного порядка.

Ответ 5

Я понимаю, что -Wall явно просит GCC переходить через верх с предупреждениями, но я предполагаю, что есть причина для всех из них.

-Wall - это просто начало. Вопреки тому, что подразумевается в названии, -Wall не включает все предупреждения. Есть некоторые предупреждения, которые, возможно, "сверху", но это именно те предупреждения, которые не разрешены. Я всегда использую -Wall плюс другие.

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

Ответ 6

Список инициализатора дает гибкость инициализации переменных в любом порядке. Однако фактическая инициализация происходит в соответствии с порядком, в котором вы объявили членов класса. Чтобы избежать этого предупреждения, вы должны изменить порядок инициализации/объявления.