Должен ли конструктор инициализировать все элементы данных класса?

У меня такая ситуация:

class A {
public:
  A() : n(0) {}
private:
  int n;
  int m;
}

В логике приложения просто нет смысла инициализировать m в конструкторе. Однако Eclipse предупреждает меня, что конструктор оставляет m неинициализированным. Я не могу запустить код в другом месте сейчас. Предупреждение:

Элемент 'm' не был инициализирован в этом конструкторе

Итак, поощряет ли C++ инициализацию всех членов данных в конструкторе или это просто логика Eclipse?


person gsamaras    schedule 14.10.2015    source источник
comment
Я бы назвал это правильной объектно-ориентированной практикой: каждая переменная должна быть инициализирована, а объект готов к использованию. Нет смысла? Тогда инициализация его значением 0, -1 или MIN_INTEGER не будет вредной.   -  person duffymo    schedule 14.10.2015
comment
Точно @duffymo. Мне просто любопытно, есть ли в стандарте что-то, что побуждает меня к этому.   -  person gsamaras    schedule 14.10.2015
comment
Использование значения неинициализированной переменной является поведением undefined. Самый безопасный подход — инициализировать каждую переменную в момент построения. Для членов класса ваши конструкторы должны гарантировать, что каждая переменная инициализирована или что у нее есть собственный конструктор по умолчанию, который делает то же самое.   -  person Andrew    schedule 14.10.2015
comment
@duffymo Я думал, что в OO (не то, чтобы что-то в вопросе говорило об OO) не должно иметь значения, что установлено для закрытого члена. Класс должен поддерживать любые инварианты, которые необходимо поддерживать.   -  person juanchopanza    schedule 14.10.2015
comment
Покажите реальный пример. Как написано, вы можете удалить оба закрытых члена, так как они не доступны для наблюдения.   -  person Kerrek SB    schedule 14.10.2015
comment
Код все равно не скомпилируется, так что нет проблем.   -  person juanchopanza    schedule 14.10.2015
comment
@juanchopanza, я так и думал! Вот почему я спрашиваю, чтобы узнать, что об этом говорит стандарт С++. Керрек, у меня их нет, я только что начал новый проект на C++, и вот что получилось! Андрей, класс знает об этом и не будет использовать неинициализированную переменную (однако в большом проекте ваш комментарий был бы очень кстати).   -  person gsamaras    schedule 14.10.2015
comment
В стандарте об этом прямо ничего не сказано. А вот чтение из неинициализированной переменной — это UB.   -  person juanchopanza    schedule 14.10.2015
comment
Конечно, @juanchopanza, я спрашиваю о том, что говорит стандарт, а не о том, что произойдет, если я прочитаю этот элемент данных. Таким образом, будет принят ответ, в котором говорится, что стандарт позволяет вам оставить элемент данных неинициализированным.   -  person gsamaras    schedule 14.10.2015
comment
Кстати, предупреждения не являются ошибками. Компилятор поможет вам указать потенциальные ошибки.   -  person Jarod42    schedule 14.10.2015
comment
Исправьте @ Jarod42, но я все же беспокоюсь о предупреждениях так же, как и об ошибках!   -  person gsamaras    schedule 14.10.2015
comment
@gsamaras Если бы стандарт не позволял этого, это была бы ошибка, а не предупреждение. Вы можете оставить все, кроме ссылочных переменных, неинициализированными. Причина предупреждения в том, что обычно это очень плохая идея.   -  person molbdnilo    schedule 14.10.2015
comment
Хорошо, @molbdnilo, спасибо!   -  person gsamaras    schedule 14.10.2015
comment
В общем, данные-члены составляют состояние объекта. В комбинаторных терминах должны быть определены все возможные начальные состояния. Вот почему я думаю, что имеет смысл убедиться, что все элементы инициализированы.   -  person lrleon    schedule 14.10.2015
comment
Спасибо @lrleon, я согласен.   -  person gsamaras    schedule 14.10.2015


Ответы (5)


Должен ли конструктор инициализировать все элементы данных класса?

Это было бы хорошей практикой.

Итак, поощряет ли C++ инициализацию всех элементов данных в конструкторе?

Это не требуется по стандарту С++. Пока вы инициализируете все переменные до того, как они будут использованы, ваша программа в этом отношении верна.

или это просто логика Эклипса?

Вполне вероятно. Ни версии g++, ни clang, которые я тестировал, не предупреждают об этом, когда все предупреждения включены. Логика может основываться или не основываться на стандарт кодирования c++ с высокой степенью целостности 12.4.2 или какой-либо другой стандарт кодирования или руководство по стилю.

person eerorika    schedule 14.10.2015

C++ не требует инициализации атрибутов в конструкторе, за исключением случаев константных атрибутов, значение которых должно быть определено в списке инициализации.

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

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

person toubab    schedule 14.10.2015
comment
Я не могу сосчитать, сколько ошибок я встретил из-за неинициализированных переменных или атрибутов - это могло быть мое собственное утверждение! - person Doc Brown; 24.11.2016

Полностью не согласен со всеми ответами и комментариями. Нет абсолютно никакой необходимости инициализировать элемент по умолчанию, когда он не нужен. Вот почему C/C++ никогда не инициализирует встроенные типы как члены или автоматические переменные, поскольку это снижает производительность. Конечно, это не проблема, когда вы создаете свой объект/переменную один раз (поэтому статика инициализируется по умолчанию), но для чего-то, что происходит в узком цикле, инициализация по умолчанию может занять ценные наносекунды.

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

person SergeyA    schedule 14.10.2015
comment
Хороший подход и +1 за то, что гордитесь тем, что идете навстречу течению. - person gsamaras; 14.10.2015
comment
Вот почему C/C++ никогда не инициализирует встроенные типы как члены или автоматические переменные. Это факт или мнение? К сожалению, этот ответ звучит как ранняя оптимизация. Ради одной машинной инструкции вы требуете, чтобы каждый будущий читатель кода удостоверился, что вы были правы, упустив ее. Как минимум, я ожидаю разумного комментария рядом с чем-то, что вызывает предупреждение о потенциальной проблеме программирования от моих инструментов анализа. Имея это в виду, я проголосовал против. - person Jonah Graham; 14.10.2015
comment
@JonahGraham, можешь уточнить? Я не уверен, что вы критикуете. Вы согласны с тем, что C/C++ не инициализирует автоматические переменные по умолчанию? Или вы согласны с предложенной причиной этого? - person SergeyA; 14.10.2015
comment
Я понимаю. Но в тех случаях, когда мне требуется определенный структурированный и непрерывный макет памяти, я бы использовал стиль C, и я бы разобрался с ситуацией с struct. Если кто-то имеет дело с объектной ориентацией, то я считаю, что лучше инициализировать все члены - person lrleon; 14.10.2015
comment
@lrleon, не уверен, что вы имеете в виду, говоря «разобраться с ситуацией со структурой» - person SergeyA; 14.10.2015
comment
Концепция класса - ОО. В этом смысле состояние объекта представляется через его элементы данных, которые должны быть инициализированы для обеспечения начального непротиворечивого состояния объекта, что, можно сказать, в ОО очень удобно; для отладки, например. Но иногда нужен не объектно-ориентированный подход, а структурированная и непрерывная структура памяти; например, сетевой пакет или фрагмент файла; в обоих случаях заинтересован в структурированном макете. В этих случаях можно использовать struct (по соглашению) и необязательно инициализировать поля, так как они не думают в ОО. - person lrleon; 14.10.2015
comment
@SergeyA Я оспариваю то, что именно поэтому часть вашего утверждения, а не тот факт, что это происходит или не происходит, а то, что это происходит или не происходит по причинам производительности (в отличие от других причин). то есть ваше утверждение подразумевает, что часть спецификаций C/C++, которая касается инициализации автоматических переменных, была самим решением, а не закреплением фактического поведения существующих компиляторов. - person Jonah Graham; 14.10.2015
comment
@SergeyA, кроме того, даже если переменная в цикле была инициализирована по умолчанию, оптимизирующий компилятор (он же современный компилятор), вероятно, оптимизировал бы инициализацию, если бы она не была нужна. то есть как в этом коде для var: for (i = 0; i ‹ N; i++) { int var; // теоретически инициализация по умолчанию равна нулю // какой-то другой код, в котором не упоминается var var = a + b; } (теоретическая) var = 0 будет оптимизирована. Так же, как если бы вы набрали int var = 0;. - person Jonah Graham; 14.10.2015
comment
Оказывается, я не знаю, как поместить фрагмент кода в комментарии. В любом случае, я был бы рад объяснить причины моего отрицательного голосования, проголосовать за него, если ответ будет обновлен, чтобы предоставить доказательства того, почему. - person Jonah Graham; 14.10.2015
comment
@JonahGraham, я не в дружеских отношениях с Бьярном Страусструппом, поэтому я не могу задать ему вопрос «почему». Я знаю такого парня, но я тоже не буду беспокоить его этим. Так что, конечно, мое «почему» — это предположение. Тем не менее, версия о том, что это было сделано из соображений эффективности, не только кажется вполне правдоподобной, но и считается общеизвестной. Например, см. stackoverflow.com/questions/2091499/. Однако я не буду просить о голосовании. - person SergeyA; 14.10.2015
comment
@SergeyA, а чем указатели отличаются от других типов? Неинициализированные данные имеют неопределенное поведение. Здесь и там. C++ позволяет вам деинициализировать переменную из соображений эффективности, это не значит, что мы должны деинициализировать вещи, иначе они будут работать медленнее. Инициализация является необходимой задачей и должна быть выполнена до использования сохраненных значений (хранение означает инициализацию), иначе вы получите мусор. Если вы считаете, что целое число не так важно, как указатель, просто используйте его в качестве индекса для массива, и вы получите закрытый указатель. И С++ также не проверяет границы массива. - person Luis Colorado; 15.10.2015
comment
Между прочим, Инициализация настолько важна, что заслуживает изобретения конструктора для этого, чтобы вы никогда не забывали это делать. - person Luis Colorado; 15.10.2015
comment
@LuisColorado, ты просто не прав. Единичные значения, как правило, НЕ ЯВЛЯЮТСЯ неопределенным поведением сами по себе - значение, помещенное в них, не определено, но вы можете использовать их как обычно. Недопустимый указатель, с другой стороны, - вы не можете его использовать никоим образом. - person SergeyA; 15.10.2015
comment
Нет абсолютно никакой необходимости инициализировать элемент по умолчанию, когда он не нужен. - снижение вероятности создания программы, которая иногда маскирует ошибку, а иногда нет, является очень хорошей причиной, по моему мнению, для инициализации любого члена по умолчанию и сохранения этой инициализации только в том случае, если такая оптимизация дает соответствующее преимущество производительности. - person Doc Brown; 24.11.2016

Для полноты предупреждение исходит из анализа кода C/C++. В частности проблема Potential Programming Problems / Class members should be properly initialized

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

показать предупреждение

Что касается сравнения CDT с GCC или CLang, похоже, это случай, когда CDT выполняет дополнительный анализ кода по сравнению с тем, что доступно от компиляторов. Конечно, этого следовало ожидать, поскольку анализ кода CDT шире, чем у компилятора.

PS, если вы готовы, вы можете прочитать реализацию этого конкретная программа проверки.

person Jonah Graham    schedule 14.10.2015
comment
Очень хорошо, если бы вы ответили первым, я бы согласился ... Честно говоря, я проголосую за другой ваш ответ, который мне нравится! :) - person gsamaras; 14.10.2015

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

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

Я запускаю Cppcheck каждые несколько месяцев. Это дает мне более сотни «ложных» предупреждений, таких как «Переменная-член« foo :: bar »не инициализирована в конструкторе». но время от времени он обнаруживает некоторые настоящие ошибки, так что оно того стоит.

person rockefelox    schedule 06.06.2016