Comments 2
Конструктор по умолчанию [...] инициализирует все члены класса согласно их типам (например, встроенные типы останутся не инициализированными, если это не
bool
, а объекты — будут инициализированы своими конструкторами по умолчанию).
Во-первых, что это за странная оговорка про bool
? Чем bool
вдруг отличается от других?
Во-вторых, что это за странное использование термина "объекты"? А что объекты встроенных типов - это не объекты?
Конструктор копирования и оператор присваивания копированием по умолчанию будет выполнять побитовую копию всех полей.
Что за дичайшая дичь? Какая еще "побитовая копия"? Никогда и нигде в С++ не делаются никакие "побитовые копии", кроме разве что std::memcpy
, которую вы сами руками вызвали .
Конструктор перемещения и оператор присваивания перемещением будут выполнять перемещение полей (перехват ресурсов).
"Перемещение" в С++ - концепция чисто пользовательского уровня. "Перемещение" может написать только вы своими руками. Конструктор перемещения и оператор присваивания перемещением будет делать именно и только то, что вы сами в нем напишете (за исключением автоматического делегирования в рамках "правила нуля"). "Встроенная" поддержка перемещения есть только у фундаментальных типов, и у них перемещение совпадает с копированием. Поэтому не ясно, о каком "будут выполнять перемещение" здесь идет речь.
По возможности стоит всегда инициализировать поля через списки инициализации
Ну вообще-то с С++11 у нас есть альтернативная возможность: указывать инициализаторы прямо в объявлениях полей, которая во многих случаях (в большинстве?) является лучшей идеей, чем ясное прописывание списков инициализации.
Первым делом обратим внимание на модификатор
explicit
. Хорошим тоном считается писать его в случаях, когда конструктор имеет лишь один параметр, потому что без него возможны неявные преобразования.
Приведенный пример кода как раз таки является примером того случая, когда позволить неявное преобразование было бы вполне уместным. Это explicit
в данном случае никак не уменьшает вероятность неправильного использования (как в приведенном далее примере).
Перемещение через std::exchange
Непонятно тогда, почему в разделе про std::exchange
при реализации оператора присваивания std::exchange
не использовалось, а использовалась std::swap
. Это совсем другая идиома. Так о какой именно идиоме вы хотели написать в этом разделе?
Перегрузка скобок
char& operator[](size_t i) { return *(str_ + i);}
Непонятно, почему в реализации используется странный инопланетный синтаксис *(str_ + i)
. Чем обычное человеческое str_[i]
не угодило?
Вектор булевых значений
char* arr_;
Для представления бинарных данных использован тип char
??? Не unsigned char
?
Да... с конструкторами по умолчанию вышел большой косяк: там я намешал всё, поэтому и вразумительным это сложно назвать. Про побитовую копию перепутал с объединениями (std::memmove
, что по сути std::memcpy
).
Инициализаторы в объявлениях полей хороши, когда несколько конструкторов используют значение по умолчанию, поэтому в данном примере нет никакой разницы, однако упомянуть про такую возможность - хорошая идея.
С explicit
получилась задачка - надеюсь пример с int
будет разумней...
А *(str_ + i)
- это неосознанное копирования кода из какой-то другой статьи.
Последнее (char
вместо unsigned char
) - это уже реальная ошибка (привык писать uint8_t
, потому забыл, что у char
сдвиги могут заполнять старшие биты единицами).
Спасибо большое за комментарий. Мне прям пришлось ещё глубже закопаться в тему, чтобы постараться исправить всё - надеюсь получилось лучше.
Конструкторы, деструкторы, операторы — частые практики при программировании на C++