Обновить

Комментарии 26

ЗакрепленныеЗакреплённые комментарии

Ну если просто списком, которые не вошли тогда так:

Скрытый текст

Hollywood Principle («don't call us, we'll call you») : инверсия управления вместо жёстких регламентов.

Customization Point Object (CPO) : расширяемость через точки настройки, а не единый «стандарт на всё».

Strong typedef / strong types : несовместимые типы не склеиваются.

Separate Compilation Model : TU + линковка как основа сборки.

Command : одна операция = один объект.

Design by Contract (Expects/Ensures) : явные пред-/постусловия.|

Dependency Inversion Principle (DIP) : верхние уровни не зависят от деталей.

Lazy Evaluation : вычисление только когда реально нужно.

Perfect Forwarding (std::forward) : дешёвая передача без копий.

Dependency Injection : зависимости передаются, а не берутся из глобала.

Monostate : «глобал», притворяющийся локальным объектом (редко, но осознанно).

Information Hiding (Parnas) : модуль знает только необходимое.

Forward Declaration : скрыть детали типа без #include.

Scoped Locking : захват/освобождение lock в паре enter/leave.

Monitor Object : данные + mutex как единый монитор.

Law of Demeter : не лезть в чужие internals через цепочки вызовов.

Unique Ownership : один владелец, явная передача.

Phantom Types : «метки» в типах, ловящие ошибки при компиляции.

Const-Correctness as API contract : const как часть контракта.

Immutable Value Objects : объект после создания не меняется.

constexpr functions / inline functions : замена макросам с типами.

Strong typedef : using Meters = strong_type<double, tag_meters>.

IILE (Immediately Invoked Lambda Expression) : локальная область + cleanup в деструкторе лямбды.

Uniform Initialization {} : нет «висячей» инициализации.

Guard Clauses / Early Return : плоская структура вместо вложенности.

Magic Statics (C++11) : thread-safe init function-local static.

Static Initialization Order Fiasco awareness : знание проблемы, даже если решаете иначе.

Include What You Use (IWYU) : включать только нужное.

Observer pointer (non-owning) : T* / std::reference_wrapper без владения.

Principle of Least Surprise для операторов : a + b не делает I/O.

Return-by-value + elision : канон для factory-функций.

Regular Type concept : тип ведёт себя «как int» для операций.

Extract to statements : вычислить аргументы в отдельные переменные до вызова.

Extract Class / Extract Interface : рефакторинг к мелким типам.

Delegation : объект делегирует, не наследует.

Composition over Inheritance : дополняй, не меняй.

Lollipop Interface : « lolli»-интерфейс, отделённый от реализации.

Pure Abstract Base Class (ABC) : классический термин до «Interface Class».

explicit conversion operators : только явные static_cast-подобные пути.

Tell, Don't Ask : поведение у объекта через getters.

Capability-based security / least privilege : минимальный API.

ADL (Argument-Dependent Lookup) : свободные функции в namespace типа.

Aligned allocation overloads : operator new(size_t, std::align_val_t).

Placement new family : in-buffer construction без heap.

Member initializer list discipline : порядок в списке ≠ порядок объявления → ошибка.

Delegating constructors : один ctor вызывает другой.

Basic/Strong exception guarantee : классификация гарантий.

Rule of Four : исторический предшественник Rule of Five.

noexcept swap guarantees : для vector, контейнеров, strong guarantee.

ADL-friendly API design : operator<<, swap, begin рядом с типом.

Namespace per library/component : границы модулей через namespace.

Qualified names in headers : std::vector, не vector.

DLL-safe allocator boundary : своя heap внутри .dll/.so.

Object Pool per module : память не пересекает границу.

ODR (One Definition Rule) : одно определение на программу.

Error codes at module boundary : HRESULT, error_code, C API.

Exception translation layer : catch internal → throw external.

POD / standard layout / trivially copyable : критерии для FFI.

C-compatible API surface (extern "C") : стабильная граница.

Static Polymorphism (templates) vs Dynamic Polymorphism (virtual) : два режима.

External Polymorphism : non-virtual полиморфизм через type erasure

Generic Programming (STL-style) : алгоритмы от итераторов, не от контейнеров.

Concepts-constrained templates : обобщение с ограничениями.

Fail-fast vs fail-safe : осознанный выбор.

Rollback / Transaction pattern : откат частичных изменений при ошибке.

Catch by const& : slicing + лишние копии.

Contiguous storage idiom : cache-friendly последовательный контейнер по умолчанию.

No raw owning pointers in containers : ownership policy.

Amortized growth strategy : понимание reallocation policy vector.

Range-based for / C++20 ranges : алгоритмы над диапазонами.

Half-open range [begin, end) : STL convention.

Iterator category requirements : complexity зависит от категории итератора.

Pure predicate functions : без скрытого state/side effects.

State-carrying lambdas / functors : захват контекста без глобала.

Regular types / Value semantics : копируемость и равенство по смыслу.

Domain-driven type naming : тип отражает домен, не layout.

Trivially copyable / standard layout checks : static_assert перед bitwise ops.

Variadic templates / fold expressions (C++17) : типизированные параметры.

Valid state invariant : объект всегда в допустимом состоянии после ctor.

No strcpy/sprintf : безопасные альтернативы (string, format).

Object Pool : переиспользование объектов вместо частых new/delete в hot path.

Arena / Linear Allocator : пакетное выделение памяти; освобождение всей области за O(1).

Observer / Publish-Subscribe : подписчики получают события без жёсткой связи с источником.

Null Object : объект-заглушка с no-op поведением вместо проверок на nullptr.

State Pattern : поведение через объект состояния, а не switch по enum.

Adapter Pattern : обёртка над чужим API под интерфейс.

Decorator Pattern : добавление поведения слоями без подклассов.

Factory Method : создание объектов через фабричный метод.

Abstract Factory : семейства связанных типов без знания конкретных классов.

Mediator Pattern : координация через посредника вместо N×N связей между объектами.

Chain of Responsibility : цепочка обработчиков, каждый решает брать задачу или передать дальше.

Double Buffering : два буфера для безопасного чтения/записи из разных потоков или кадров.

Struct of Arrays (SoA) : data-oriented layout поля сущностей разнесены по массивам, не AoS.

Amalgamated Header (stb-style) : один .h + #define IMPLEMENTATION в одном .cpp.

Overload Pattern : набор лямбд/функторов для std::visit и подобной диспетчеризации.

Deduction Guides (C++17) : явный вывод типа шаблона из аргументов конструктора.

std::declval : «фиктивная» lvalue-ссылка на тип без его конструирования

Type-safe Bitmask Enum : enum class + перегрузки |, &, ~ без неявного int.

Copy Elision / Guaranteed RVO (C++17) : возврат локального объекта без лишней копии по стандарту.

Reference Wrapper (std::reference_wrapper) : nullable-ссылка в контейнерах и колбэках, где T& нельзя.

Railway-Oriented Programming : цепочка операций, где ошибка ломает дальнейший путь (expected/optional + early return).

Вот это прикол. Дочитал до Pimpl (Handle Body, Compilation Firewall, Cheshire Cat) и немного устал. Посмотрел на бегунок и не поверил! Прочитал значительно меньше половины. Отложил в закладки, дочитаю потом. Статья интересная, но уже поздно.

Это вы устали читать :) шутка. А Мейерс с Александреску половину этого придумали

Для тех, кто не первый год в с++, это просто обыденность. Правда, повторить азы тоже полезно)

А ведь нас предупреждали что это половина книги..

За всё надо платить, и я заплатил своим временем за прочтение этой статьи.

Фундаментальный труд. Respect!

Обсужу с коллегами ачивку ))

О, какая шикарная статья на Хабре, наконец-то. Посреди всего этого нейрослопа о нейрослопе и нечастых, но вынужденно полезных статей об обходе блокировок, словно вернулись старые добрые времена, когда Программирование - это было что-то такое с большой буквы, когда еще мир был другим, а не вот это вот всё что сейчас...

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

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

Странный заголовок с числом ни разу не упомянутым в статье.

Это идиома такая. Самому лень расписывать. Вот от Gemini, что называется, одной строкой:

В американской академической системе индекс «101» традиционно означает вводный или базовый курс по какому-либо предмету. В повседневной речи и книгах выражение «что-то 101» стало идиомой, обозначающей самые азы или фундаментальные основы чего-либо (например, English 101 — основы английского языка).

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

Разных базовых идей, понятий и механизмов, с которыми сталкивается с++ разработчик (да и не только с++) больше

Кстати было бы интересно ознакомиться с максимально полным списком:) Хотя-бы просто список названий, чтобы дальше догуглить самостоятельно. Интересно, кто нибудь вообще пытался максимально собрать и систематизировать именно идеомы программирования как таковые?

Ну если просто списком, которые не вошли тогда так:

Скрытый текст

Hollywood Principle («don't call us, we'll call you») : инверсия управления вместо жёстких регламентов.

Customization Point Object (CPO) : расширяемость через точки настройки, а не единый «стандарт на всё».

Strong typedef / strong types : несовместимые типы не склеиваются.

Separate Compilation Model : TU + линковка как основа сборки.

Command : одна операция = один объект.

Design by Contract (Expects/Ensures) : явные пред-/постусловия.|

Dependency Inversion Principle (DIP) : верхние уровни не зависят от деталей.

Lazy Evaluation : вычисление только когда реально нужно.

Perfect Forwarding (std::forward) : дешёвая передача без копий.

Dependency Injection : зависимости передаются, а не берутся из глобала.

Monostate : «глобал», притворяющийся локальным объектом (редко, но осознанно).

Information Hiding (Parnas) : модуль знает только необходимое.

Forward Declaration : скрыть детали типа без #include.

Scoped Locking : захват/освобождение lock в паре enter/leave.

Monitor Object : данные + mutex как единый монитор.

Law of Demeter : не лезть в чужие internals через цепочки вызовов.

Unique Ownership : один владелец, явная передача.

Phantom Types : «метки» в типах, ловящие ошибки при компиляции.

Const-Correctness as API contract : const как часть контракта.

Immutable Value Objects : объект после создания не меняется.

constexpr functions / inline functions : замена макросам с типами.

Strong typedef : using Meters = strong_type<double, tag_meters>.

IILE (Immediately Invoked Lambda Expression) : локальная область + cleanup в деструкторе лямбды.

Uniform Initialization {} : нет «висячей» инициализации.

Guard Clauses / Early Return : плоская структура вместо вложенности.

Magic Statics (C++11) : thread-safe init function-local static.

Static Initialization Order Fiasco awareness : знание проблемы, даже если решаете иначе.

Include What You Use (IWYU) : включать только нужное.

Observer pointer (non-owning) : T* / std::reference_wrapper без владения.

Principle of Least Surprise для операторов : a + b не делает I/O.

Return-by-value + elision : канон для factory-функций.

Regular Type concept : тип ведёт себя «как int» для операций.

Extract to statements : вычислить аргументы в отдельные переменные до вызова.

Extract Class / Extract Interface : рефакторинг к мелким типам.

Delegation : объект делегирует, не наследует.

Composition over Inheritance : дополняй, не меняй.

Lollipop Interface : « lolli»-интерфейс, отделённый от реализации.

Pure Abstract Base Class (ABC) : классический термин до «Interface Class».

explicit conversion operators : только явные static_cast-подобные пути.

Tell, Don't Ask : поведение у объекта через getters.

Capability-based security / least privilege : минимальный API.

ADL (Argument-Dependent Lookup) : свободные функции в namespace типа.

Aligned allocation overloads : operator new(size_t, std::align_val_t).

Placement new family : in-buffer construction без heap.

Member initializer list discipline : порядок в списке ≠ порядок объявления → ошибка.

Delegating constructors : один ctor вызывает другой.

Basic/Strong exception guarantee : классификация гарантий.

Rule of Four : исторический предшественник Rule of Five.

noexcept swap guarantees : для vector, контейнеров, strong guarantee.

ADL-friendly API design : operator<<, swap, begin рядом с типом.

Namespace per library/component : границы модулей через namespace.

Qualified names in headers : std::vector, не vector.

DLL-safe allocator boundary : своя heap внутри .dll/.so.

Object Pool per module : память не пересекает границу.

ODR (One Definition Rule) : одно определение на программу.

Error codes at module boundary : HRESULT, error_code, C API.

Exception translation layer : catch internal → throw external.

POD / standard layout / trivially copyable : критерии для FFI.

C-compatible API surface (extern "C") : стабильная граница.

Static Polymorphism (templates) vs Dynamic Polymorphism (virtual) : два режима.

External Polymorphism : non-virtual полиморфизм через type erasure

Generic Programming (STL-style) : алгоритмы от итераторов, не от контейнеров.

Concepts-constrained templates : обобщение с ограничениями.

Fail-fast vs fail-safe : осознанный выбор.

Rollback / Transaction pattern : откат частичных изменений при ошибке.

Catch by const& : slicing + лишние копии.

Contiguous storage idiom : cache-friendly последовательный контейнер по умолчанию.

No raw owning pointers in containers : ownership policy.

Amortized growth strategy : понимание reallocation policy vector.

Range-based for / C++20 ranges : алгоритмы над диапазонами.

Half-open range [begin, end) : STL convention.

Iterator category requirements : complexity зависит от категории итератора.

Pure predicate functions : без скрытого state/side effects.

State-carrying lambdas / functors : захват контекста без глобала.

Regular types / Value semantics : копируемость и равенство по смыслу.

Domain-driven type naming : тип отражает домен, не layout.

Trivially copyable / standard layout checks : static_assert перед bitwise ops.

Variadic templates / fold expressions (C++17) : типизированные параметры.

Valid state invariant : объект всегда в допустимом состоянии после ctor.

No strcpy/sprintf : безопасные альтернативы (string, format).

Object Pool : переиспользование объектов вместо частых new/delete в hot path.

Arena / Linear Allocator : пакетное выделение памяти; освобождение всей области за O(1).

Observer / Publish-Subscribe : подписчики получают события без жёсткой связи с источником.

Null Object : объект-заглушка с no-op поведением вместо проверок на nullptr.

State Pattern : поведение через объект состояния, а не switch по enum.

Adapter Pattern : обёртка над чужим API под интерфейс.

Decorator Pattern : добавление поведения слоями без подклассов.

Factory Method : создание объектов через фабричный метод.

Abstract Factory : семейства связанных типов без знания конкретных классов.

Mediator Pattern : координация через посредника вместо N×N связей между объектами.

Chain of Responsibility : цепочка обработчиков, каждый решает брать задачу или передать дальше.

Double Buffering : два буфера для безопасного чтения/записи из разных потоков или кадров.

Struct of Arrays (SoA) : data-oriented layout поля сущностей разнесены по массивам, не AoS.

Amalgamated Header (stb-style) : один .h + #define IMPLEMENTATION в одном .cpp.

Overload Pattern : набор лямбд/функторов для std::visit и подобной диспетчеризации.

Deduction Guides (C++17) : явный вывод типа шаблона из аргументов конструктора.

std::declval : «фиктивная» lvalue-ссылка на тип без его конструирования

Type-safe Bitmask Enum : enum class + перегрузки |, &, ~ без неявного int.

Copy Elision / Guaranteed RVO (C++17) : возврат локального объекта без лишней копии по стандарту.

Reference Wrapper (std::reference_wrapper) : nullable-ссылка в контейнерах и колбэках, где T& нельзя.

Railway-Oriented Programming : цепочка операций, где ошибка ломает дальнейший путь (expected/optional + early return).

Free function allocators мой любимый. Помню давным давно на заре игростроя в РФ, мы его в самописном движке использовали. При старте игра выделяла сразу большой кусок памяти в куче, а потом через new мы все обьекты создавали в этом куске памяти. Даже свое фрагментирование было. Никаких утечек памяти и шустро работало. Автору респект, редко доводится почитать что-то действительно интересное и полезное на Хабре.

Отличная статья. Но за все надо платить, и в ней очень много букв

Ага, как оказалось платить в С++ приходится за всё, даже за бесплатное и за Zero-cost, но язык даёт контроль над тем, кто платит, когда и чем.

Статья огонь. Один момент не понятен.

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

Так маленький буфер кладется на место указателя на динамическую память. В этом же и смысл SSO, что пока данные маленькие, можем использовать место выделенное под указатель. Или я что-то не понимаю?

В пределе строку можно ужать до одного указателя и 8 байт, но это слишком мало и размер и capacity тогда приходится держать в куче, рядом с самими данными. Объект (строка) выходит крошечный, массивы таких строк плотно ложатся в кеш, но за каждое обращение к длине или ёмкости платим индирекцией и лишним походом в память.
Чтобы её убрать, размер и capacity вытаскивают обратно в сам объект, рядом с указателем.
Дальше работает правило (для строк) N байт SSO-буфер вмещает N−1 символ и отсюда два типовых расклада: либо { char* + uint32 size + uint32 cap } и 16 байт, 15 символов inline.
Либо { size_t cap; size_t size; char* data; } и 24 байта, и при удачной упаковке 23 символа + терминатор.

23 символа звучит много, но на практике это покрывает где 40% строк, значит остальные 60% будут болтаться в памяти. А хочешь больше, ну скажем, 60% под ногами и нужен буфер символов эдак на 40, а это объект уже за 40 байт. И вот тут и приходит та самая расплата, чтто 40 байт против 8 или даже 24 уже жирный объект и две такие строки в одну кеш-линию (64 байта) уже не положить, и при проходе будет чтение из другой кешлинии.

Получается дилемма: либо мелкий объект и плотные массивы, но всего ~8 байт под SSO и индирекция на длинных строках, либо большой SSO-буфер и быстрый доступ к содержимому, но жирные строки, которые уже в кеш-линию не лезут.
Я на строках объяснил, но для всего остального суть таже. Нужный размер нужно мерять для своего домена применения, про 40 символов строки я могу сказать об играх, потому там есть замеры.


Кстати, в статье не упомянуто, но в С++23 на замену CRTP пришел dedusing this, который позволяет писать миксины более понятно и менее многословно

Я вообще не специалист по плюсам, никогда не писал продуктовый код на нем. Сейчас плотно сижу изучаю Rust на примере своего пет-проекта. Скажите насколько все эти вещи актуальны для Rust? Какая часть из этих идиом не имеют смысла в Rust?

В Rust есть прямые аналоги некоторых из идиом типа RAII / move semantics / slices. Часть ключевых особенностей выглядит, как осмысленное заимствование опыта C/C++. Часть совершенно неактуальна (include guards, большая часть шаблонной магии, ....).

Наверное, интересно прослеживать кросс-языковые идеи, но в rust складываются свои идиомы, местами перекликающиеся с плюсовыми (https://rust-unofficial.github.io/patterns/intro.html)

Извините за кучу синтаксических ошибок, сколько не вычитывал, а все равно остались.
Огромная благодарность @Mingunза правку текста.

Да, завтра (а может, и послезавтра) возможно еще прилетит :). Давненько не было таких фундаментальных трудов на Хабре, последний что помню – Галоп пикселя (@Weilard кстати обещал вернуться с еще одной частью… но не вернулся еще). Сегодня практически весь день статью читал и все равно еще 8 пунктов осталось. Таймер времени над статьей явно врет :).

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

Спасибо, прочитал, и теперь абсолютно уверенно могу сказать - я не знаю C++ 😅

Я пока не посмотрел лекции Константина Владимирова, тоже был уверен что знал С++, теперь не уверен :) https://www.youtube.com/watch?v=9N_wJ7oIHDk

Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации