Вопрос по compiler-warnings, c++, g++ – Почему я должен инициализировать переменные-члены в порядке, в котором они объявлены?

53

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

Пример:

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 опередить предупреждения, но я предполагаю, что для всех них есть причина. Итак, как может иметь значение порядок инициализации переменных-членов?

Ваш Ответ

5   ответов
31

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

Если вы сделали:

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

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

+1 для примера, чтобы показать, почему порядок действительно имеет значение и что может пойти не так, если кто-то предполагает, что порядок - это то, что он объявил в конструкторе.
80

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

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

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

12

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

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

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

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

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

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

5

I realize that -Wall is explicitly asking GCC to go over-the-top with warnings, but I assume there's a reason for all of them.

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

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

38

Why should I initialize member variables in the order they're declared in?

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

@XavierHolt: В языке для всех объектов, кроме динамически размещаемых, порядок построения и уничтожения меняется на противоположный. Если бы порядок инициализации определялся списком инициализаторов, то порядок инициализации в классе с несколькими конструкторами не был бы определен, и порядок уничтожения членов также не был бы определен. Причина, по которой порядок строительства / разрушения меняется на противоположный, заключается в том, чтобы гарантировать, что если объект зависит от другого, второй не будет уничтожен раньше первого.
@ DavidRodr & gt; guez-dribeas - Ага! Тотis хорошая причина - и я бы не подумал. Спасибо!
+1 для краткого, точечного ответа, в котором не используется слово «компилятор».
Хороший ответ. Можем ли мы предположить, что это всего лишь стандарт C ++, обеспечивающий детерминированную компиляцию всех конструкторов, или есть какая-то техническая / производственная причина, по которой члены всегда должны инициализироваться в том порядке, в котором они объявлены?

Похожие вопросы