LINUX.ORG.RU

Инициализация полей класса

 ,


0

1

Читаю книгу Beautiful C++. Там рассматривается вопрос о правильной инициализации полей класса и в качестве начального примера используется следующий код:

class piano
{
public:
  piano();
private:
  int number_of_keys;
  bool mechanical;
  std::string manufacturer;
};

piano::piano()
{
  number_of_keys = 88;
  mechanical = true;
  manufacturer = "Yamaha";
}

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

piano::piano()
  : number_of_keys(88)
  , mechanical(true)
  , manufacturer("Yamaha")
{}

Мне более привычен немного другой стиль написания:

piano::piano() :
    number_of_keys(88),
    mechanical(true),
    manufacturer("Yamaha")
{}

А вот ещё нашёл документ, описывающий гугловский стиль написания кода на C++ https://google.github.io/styleguide/cppguide.html#Constructor_Initializer_Lists и в нём этот код выглядел бы вот так:

piano::piano()
    : number_of_keys(88),
      mechanical(true),
      manufacturer("Yamaha") {
}

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

P.S. на этом разбор инициализации в книге не заканчивается, но меня заинтересовал стиль написания кода.



Последнее исправление: zg (всего исправлений: 1)
Ответ на: комментарий от kvpfs_2

Если есть вычитание в правой части, то беззнаковый тип в левой это очень подозрительно выглядит.

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

В целом, мне идея {} нравится, и я ее по возможности использую.

soomrack ★★★★★
()
Ответ на: комментарий от soomrack

Идея - супер, правильная. Но эта тухлая диагностика … . Ну зачем её было в стандарт, оно тянет на опциональную диагностику компилятора. А так получается, взяв на вооружение всю эту концепцию с narrow conv, нужно писать такую лабуду для консистентности:

void f(int i) {}

int main() {
    double d = 4.3;
    f({d}); // отлично, получаем предупреждение, без {} - не получили бы
}

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

В общем подожду, пока разум возобладает и данная диагностика отправится к garbage collector support.

kvpfs_2
()
Ответ на: комментарий от kvpfs_2

В этом примере, кмк, предупреждение правильное. Типы данных разные, есть неявное приведение типов. Но это просто мое желание иметь более строгую типизацию в С++, чем та, которая есть, чтобы если что-то неявно происходит, что потенциально может иметь последствия, то выкидывался бы ворнинг (например, если неявно double в int, то ворнинг, а если неявно short int в int, то все норм).

Поэтому в данном случае я бы писал f(d) или прямо бы указал приведение к типу так, как хочу: f(static_cast<int>(d)).

soomrack ★★★★★
()
Ответ на: комментарий от soomrack

Visual Studio 2022 17.10.3

Вот здесь https://www.youtube.com/watch?v=Dg77_dp8jgE чувак пишет в Qt Creator тоже под виндой, судя по логам в MingGW и на 4:44 он рассказывает и наглядно показывает то, что фигурная инициализация запрещает сужающие преобразования.

У меня в MSYS2 g++ 14.1.0 по умолчанию выдаёт лишь предупреждение, если только не добавить опцию -pedantic-errors. Возможно Qt Creator включает эту опцию по умолчанию.

zg
() автор топика
Ответ на: комментарий от AntonI

Главное что не аннунаки с аннунашками, а то заставят мегалитически оформлять код - заманаешься зубилом стучать;-)

Данупохи с форматированием не заморачиваются и как-то лазерная хлеборезка у них работает))

PRN
()
Ответ на: комментарий от soomrack

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

Так ведь в том и дело, что f(d) с неявным кастом дабл в инт не даст никакого воринга, тебе нужно писать вот этот жуткий костыль f({d}), чтобы следовать данной концепции, которую ты принял. Иначе у тебя огромная брешь, это как быть ярым сторонником ЗОЖ, но при этом по выходным в хлам упарываться героином. Ты ведь не станешь паковать все аргументы функций в {}? Ведь да? В общем поздно такие штуки в стандарт тащить, применительная практика сложилась, теперь таким вещам место только в качестве опциональных флагов компилятору, где параноики могут получать ошибку даже в случае f(d).

f(static_cast(d))

Опять это все такие жуткие костыли. Без обсуждения того, что голый static_cast для таких целей - нельзя, ибо мест таких будет много, значит будет потерян смысл конструкций из семейства .*_cast<>(), когда можно грепнуть код и посмотреть на все эти места под лупой, значит надо делать обертки make_int/signed/unsigned/…, в общем геморрой на ровном месте. Так ещё и шаблонный код существует, и конкретные типы/размерности могут быть неизвестны, а значит с ненулевой вероятностью в шаблонном коде будут уместны конструкции вроде:

typename <typename F, typename F_traits>
void action(F f) {
    f(make_int<F_traits::arg_type>(d))
}

В общем не нужна никакая более строгая типизация в плюсах, ибо больно и все привыкли иначе. Доп флаги для параноиков - отлично, но не в стандарт. Лично мое мнение, не переубеждаю

kvpfs_2
()
Ответ на: комментарий от kvpfs_2

Ты ведь не станешь паковать все аргументы функций в {}?

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

soomrack ★★★★★
()
Ответ на: комментарий от soomrack

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

Вот на этом и надо было остановиться стандартописателям, кому там моча в голову ударила с проверкой на narrow conversion …, в итоге загадили вполне годную и полузную идею, когда читателю кода это давало бы полезные подсказки

kvpfs_2
()
Ответ на: комментарий от andreyu

Там нужен или virtual деструктор, или класс должен быть final.

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

Ivan_qrt ★★★★★
()
Ответ на: комментарий от andreyu

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

Меньше кода – проще сопровождение и проще рефакторинг.

soomrack ★★★★★
()