# Конструктор по умолчанию и = default Гайдлайны по современному C++ всячески намекают, а иногда напрямую советуют: [следуйте "правилу нуля" (rule of zero)](https://en.cppreference.com/w/cpp/language/rule_of_three) для ваших классов, и структур и будет вам счастье! Используйте инициализаторы по умолчанию! C++20 улучшил поддержку структур-аггрегатов, так что не надо писать вручную конструкторы там где это не надо... Но legacy код существует, его затратно переписывать... А также существуют legacy разработчики, которые застряли в C++98... Так что в старых кодовых базах можно встретить что-нибудь такое: ```C++ // Point.hpp class Point2D { public: Point2D(int _x, int _y); // Раз добавили какой-то конструктор, // нужно добавить и конструктор по умолчанию Point2D(); int x; int y; }; // Некоторые разработчики как мантру твердят, что // определение любых функций всегда нужно выносить // в компилируемый .cpp файл. Даже коротких. // Point.cpp Point2D::Point2D(int _x, int _y) : x {_x}, y {_y} {} // И даже такие! Point2D::Point2D() = default; ``` Делать так в современном C++ крайне не рекомендуется. Не только из-за обилия бессмысленного бойлерплейта, но и из-за риска получить неинициализированные поля и неопределенное поведение вместе с ними. Инициализация в C++ -- невероятно сложная тема из-за обилия терминологии, переопределенного синтаксиса и вариативности, чтоб удовлетворить все мыслимые и немыслимые возможности. А также из-за множества особых случаев и исключений. И с подобным устаревшим подходом к описанию конструкторов как раз связано одно из таких исключений. Пусть нам все-таки очень нужно иметь конструкторы для точки И мы их определили в составе объявления класса ```C++ class Point2D { public: Point2D(int _x, int _y) : x {_x }, y {_y} {} // Раз добавили какой-то конструктор, // нужно добавить и конструктор по умолчанию Point2D() = default; int x; int y; }; ``` И мы создаем точку, инициализированную по умолчанию с помощью фигурных скобок, как рекомендуется в современном C++ ```C++ int main() { Point2D a {}; return a.x; } ``` Стандарт гарантирует, что произойдет zero initialization. Потому как в классе из тривиальных типов без инициализаторов `Point2D() = default` определил тривиальный конструктор по умолчанию. Так что все здорово. Никаких неинициализированных полей. Но стоит нам вынести определение конструктора по умолчанию за пределы объявления класса ```C++ class Point2D { public: Point2D(int _x, int _y) : x {_x }, y {_y} {} Point2D(); int x; int y; }; Point2D::Point2D() = default; ``` Как все резко поменяется! Теперь это уже нетривиальный конструктор. А значит инициализация фигурными скобками должна вызвать его вместо zero initialization. И поля `x`, `y` останутся неинициализированными. Ведь мы их не инициализировали. ```C++ struct Bad { int x; Bad(); }; Bad::Bad() = default; struct Good { int x; Good() = default; }; int main() { Bad a {}; Good b {}; return a.x + b.x; } ``` При компиляции GCC c `-std=c++26 -O3 -Wall -Wextra -Wpedantic -Wuninitialized` Мы получим предупреждение ``` :15:14: warning: 'a.Bad::x' is used uninitialized [-Wuninitialized] 15 | return a.x + b.x; ``` Стоит отметить, что без оптимизаций, ни GCC 14, ни Clang 18 предупреждений [не выдают](https://godbolt.org/z/z1v3bEPEq). Ну хорошо. Класс для 2D точки это все-таки отличный кандидат, чтоб просто использовать аггрегаты и списки инициализации и не думать. Да. Делайте так! ```C++ struct Point2D { int x = 0; int y = 0; }; ``` Я также встречал эту проблему и в более сложных случаях: Был класс для логгирования: ```C++ class Logger { public: Logger(std::string log_group) Logger(); // определен как Logger::Logger() = default в .cpp файле private: // Это поле было в классе давно. У строк есть конструктор по умолчанию // инициализатор не обязателен std::string log_group; }; ``` В какой-то момент было решено добавить поле для контроля максимальной длины строки ```C++ class Logger { public: Logger(std::string log_group, size_t limit) Logger(); private: std::string log_group; size_t limit; // Неопытный программист, // которому поручили задачу, по аналогии добавил поле без инициализатора }; ``` Все компилируется, но логгер по умолчанию перестает работать, а `= default` сбивает программиста с толку. Инициализируйте поля явно! Всегда, кроме случаев, когда инициализация действительно становится проблемой для производительности.