Порядок методов класса и использование результата
Этот контракт применяется к собственному нативному коду Engine и расширениям проекта. Сторонний код сохраняет правила своего владельца.
Пассивные записи и классы
Используйте struct только для пассивных записей с открытыми данными. Не объявляйте
в них конструкторы, деструкторы, методы и операторы-члены, включая явно заданные
через = default специальные методы и сравнения. Они не наследуют поведение,
не обеспечивают инварианты и не используют final. Инициализируйте поля записи
фигурными скобками, используя {} для начального состояния по умолчанию;
см. инициализацию.
Функции создания, чтения, сравнения и изменения
записей размещайте снаружи как свободные функции; где уместно, используйте
свободные операторы.
Ограничение пассивными записями не применяется к Source/Essentials/.
Структуры там могут иметь конструкторы, методы и операторы-члены, в том числе
базовые типы значений, внутренние записи данных и шаблонные адаптеры интерфейсов.
Сохраняйте их естественный интерфейс; не заменяйте его свободными функциями
и не меняйте ключевое слово только ради правила пассивных записей.
Типы настроек (включая генерируемые группы, базовые и объединённые настройки)
и все типы с меткой ExportValueType также исключены из ограничения во всех
нативных слоях и расширениях проекта. Сохраняйте конструкторы, методы,
операторы-члены и естественный интерфейс данных таких структур. Например,
mpos, msize и TextPackKey сохраняют методы, а нативные настройки —
доступ settings.Group.Name. Исключение не требует переводить существующий
класс в структуру.
Если типу нужны контролируемое состояние или собственный
протокол времени жизни, используйте class. Инкапсулируйте представление, предоставляйте
только необходимый публичный интерфейс, оставляйте детали реализации приватными.
Замена ключевого слова с последующим открытием всех полей не создаёт
инкапсуляцию. Выбирайте тип по его ответственности; не добавляйте геттеры лишь
ради сохранения прежнего синтаксиса методов записи. Для классов действуют
приведённые ниже правила порядка методов и использования результата.
Пустые строки перед комментариями
Комментарий сам по себе нигде не требует пустой строки перед ним, включая тела функций и лямбд, перечисления и списки членов структур и классов. Выбирайте пустые строки по смысловой группировке кода и объявлений. Комментарии размещайте непосредственно перед описываемым кодом. Правила разделения смысловых этапов продолжают действовать.
Порядок методов класса
Сначала размещаются конструирование, присваивание и уничтожение. За ними идёт верхний публичный блок запросов о состоянии объекта или представляемых им данных. Далее размещаются операции от инициализации к завершению работы. Сохраняйте уровни доступа и группируйте связанные методы внутри соответствующего блока. Сохраняйте исходный взаимный порядок внутри каждого блока; перемещайте только методы, необходимые для такого разделения. Вложенные типы и псевдонимы должны быть доступны использующим их объявлениям.
Информационный метод отвечает на вопрос об объекте: его свойствах, содержимом, доступных записях, текущем статусе или связанных объектах. Он может вернуть поле, значение, владеющую строку или контейнер, заимствованный указатель, ссылку, span или итератор. Запрос может искать, сравнивать данные, получать производное значение, читать хранилище, брать блокировку или заполнять внутренний кеш. Эти детали реализации не исключают его из информационных методов. Предусловия и возврат изменяемого заимствованного указателя или ссылки тоже не мешают. Классу без таких запросов информационный блок не нужен.
Изменение состояния, потребление входных данных с продвижением курсора, создание
ресурса, ожидание работы и существенная обработка как основное назначение метода
являются операциями. Они идут ниже информационного блока, даже если имеют const
или возвращают значение. Оценивайте логический контракт и наблюдаемые побочные
эффекты, не объявляя каждый поиск, выделение памяти или ввод-вывод отдельной
операцией. Если назначение неясно, читайте реализацию и вызываемые функции.
Виртуальные объявления классифицируются по контракту; простая заглушка
не превращает операцию в информационный метод.
Например, CacheStorage::HasEntry(), GetString(), GetData() и
GetDataBounded() запрашивают информацию, даже если ищут или читают файл
и возвращают данные с выделением памяти. ConfigFile::HasSection() и
FileHeader::GetNameNoExt() тоже относятся к информационному блоку.
CacheStorage::SetDataChecked() меняет хранимые данные, FileHeader::Copy()
явно создаёт копию, а FileReader::ReadUInt8() сдвигает курсор чтения: это операции.
[[nodiscard]]
В Source/Essentials/ ставьте [[nodiscard]] везде, где игнорирование результата
противоречит назначению функции или теряет необходимое значение, ресурс либо
статус ошибки. Сюда относятся подходящие свободные функции, фабрики, функции
преобразования и арифметики, результаты ввода-вывода. Изменяющим методам с
цепочками вызовов и операторам присваивания и потоков, чей результат используется
только для необязательного продолжения цепочки, атрибут не нужен лишь из-за
возвращаемого значения.
Вне Essentials используйте [[nodiscard]] только у публичных методов экземпляра
в верхнем информационном блоке; помечайте все методы этого блока. По умолчанию
сохраняйте существующие атрибуты запросов; для снятия нужна конкретная
семантическая причина.
Убирайте атрибут у операций, свободных функций, статических методов, непубличных
помощников, определений вне класса и типов. Не переносите операцию в этот блок
ради атрибута. Имя Get/Is, тип результата, const или noexcept не являются
критерием. Смешанные семейства методов, создаваемые макросами, нельзя помечать
целиком, когда отдельные раскрытия изменяют данные или выполняют операции.
При намеренном игнорировании помеченного результата используйте явное приведение
к (void) в месте вызова, предварительно проверив допустимость игнорирования.
Не подавляйте предупреждение, обнаружившее необработанную ошибку или потерю владения.
При переносе объявлений проверяйте ограничения шаблонов, комментарии, условные ветви и уровни доступа. Сохраняйте реализации, значения по умолчанию и порядок полей. При переносе виртуальных объявлений пересобирайте нативных потребителей вместе; атрибут и группировка объявлений не меняют сериализацию и сетевые форматы.
Связанные контракты: локальные переменные, безопасность исключений и Essentials.