Комментарии 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 мы все обьекты создавали в этом куске памяти. Даже свое фрагментирование было. Никаких утечек памяти и шустро работало. Автору респект, редко доводится почитать что-то действительно интересное и полезное на Хабре.
Отличная статья. Но за все надо платить, и в ней очень много букв
Статья огонь. Один момент не понятен.
Расплачиваться приходится сложностью реализации и бОльшими размерами класса, который специально раздувают чтобы поместить побольше данных
Так маленький буфер кладется на место указателя на динамическую память. В этом же и смысл 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)
Да, завтра (а может, и послезавтра) возможно еще прилетит :). Давненько не было таких фундаментальных трудов на Хабре, последний что помню – Галоп пикселя (@Weilard кстати обещал вернуться с еще одной частью… но не вернулся еще). Сегодня практически весь день статью читал и все равно еще 8 пунктов осталось. Таймер времени над статьей явно врет :).
С такими длинными статьями даже опасаешься, что плюс не успеешь поставить, пока до конца доберешься, таймер уже и уйдет.
Спасибо, прочитал, и теперь абсолютно уверенно могу сказать - я не знаю C++ 😅
Я пока не посмотрел лекции Константина Владимирова, тоже был уверен что знал С++, теперь не уверен :) https://www.youtube.com/watch?v=9N_wJ7oIHDk

C++101