<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Refactoring on kenji.blog</title><link>http://kenji.blog/ru/tags/refactoring/</link><description>Recent content in Refactoring on kenji.blog</description><generator>Hugo -- gohugo.io</generator><language>ru</language><copyright>kenjinote</copyright><lastBuildDate>Sat, 12 Sep 2026 20:00:00 +0900</lastBuildDate><atom:link href="http://kenji.blog/ru/tags/refactoring/index.xml" rel="self" type="application/rss+xml"/><item><title>Секреты рефакторинга: безопасное улучшение устаревшего кода C++</title><link>http://kenji.blog/ru/p/refactoring-legacy-cpp-code-safely/</link><pubDate>Sat, 12 Sep 2026 20:00:00 +0900</pubDate><guid>http://kenji.blog/ru/p/refactoring-legacy-cpp-code-safely/</guid><description>&lt;img src="http://kenji.blog/p/refactoring-legacy-cpp-code-safely/img/eyecatch.jpg" alt="Featured image of post Секреты рефакторинга: безопасное улучшение устаревшего кода C++" />&lt;h1 id="секреты-рефакторинга-безопасное-улучшение-устаревшего-кода-c">Секреты рефакторинга: безопасное улучшение устаревшего кода C++
&lt;/h1>&lt;p>В современной разработке программного обеспечения битва с &amp;ldquo;устаревшим кодом&amp;rdquo; (legacy code) неизбежна. Особенно в таком языке, как C++, устаревший код представляет собой угрозу, несравнимую с другими языками. Ручное управление памятью (шторм сырых указателей и &lt;code>new&lt;/code> / &lt;code>delete&lt;/code>), злоупотребление глобальными переменными, отсутствие безопасности исключений и, что важнее всего, тот факт, что &amp;ldquo;нет тестов&amp;rdquo;. Майкл Физерс в своей известной книге «Эффективная работа с унаследованным кодом» категорично заявил: «Код без тестов — это унаследованный (legacy) код».&lt;/p>
&lt;p>В этой статье мы подробно и всесторонне, как с теоретической, так и с практической точек зрения, объясним секреты безопасного и надежного перехода базы устаревшего кода C++, накопленной десятилетиями, на Modern C++ (C++11/14/17/20) и ее рефакторинга. Мы охватим практические подходы, начиная с математических моделей технического долга, безопасного разделения зависимостей и заканчивая очисткой кода с использованием современных языковых функций.&lt;/p>
&lt;hr>
&lt;h2 id="1-математическая-модель-сложности-и-технического-долга">1. Математическая модель сложности и технического долга
&lt;/h2>&lt;p>Чтобы оправдать рефакторинг, необходимо количественно оценить проблемы, присущие текущей кодовой базе. Наиболее распространенным показателем для измерения структурной сложности кода является &amp;ldquo;цикломатическая сложность&amp;rdquo; (Cyclomatic Complexity). Эта сложность определяется следующей формулой, основанной на теории графов для графа потока управления (control flow graph).&lt;/p>
$$ M = E - N + 2P $$&lt;p>Где:&lt;/p>
&lt;ul>
&lt;li>$M$ — цикломатическая сложность&lt;/li>
&lt;li>$E$ — количество ребер (потоков обработки, переходов) в графе&lt;/li>
&lt;li>$N$ — количество узлов (базовых блоков обработки) в графе&lt;/li>
&lt;li>$P$ — количество компонент связности (обычно для одной функции или метода $P=1$)&lt;/li>
&lt;/ul>
&lt;p>Чем больше сложность $M$, тем количество тестовых случаев, необходимых для исчерпывающего тестирования этой функции, увеличивается линейно или, в зависимости от комбинации условных ветвлений, экспоненциально. Кроме того, существует эмпирическое правило, согласно которому вероятность возникновения ошибки $P(bug)$ возрастает экспоненциально по отношению к сложности $M$. Если мы смоделируем это в форме, похожей на распределение Пуассона, получится следующее:&lt;/p>
$$ P(bug) = 1 - e^{-\lambda \cdot M} $$&lt;p>(Где $\lambda$ — это константа, зависящая от навыков команды разработчиков и сложности предметной области.)&lt;/p>
&lt;p>Также стоимость технического долга растет по принципу сложных процентов. Если первоначальный технический долг равен $C_0$, а процентная ставка за итерацию (процент снижения производительности из-за сложности изменения кода) равна $r$, стоимость доработки $Cost(t)$ через $t$ периодов можно выразить следующим образом:&lt;/p>
$$ Cost(t) = C_0 \times (1 + r)^t $$&lt;p>Эта формула ясно демонстрирует жестокий факт: &amp;ldquo;игнорирование устаревшего кода приводит к экспоненциальному росту затрат с течением времени&amp;rdquo;. Следовательно, долг необходимо погашать (рефакторить) на ранних этапах.&lt;/p>
&lt;hr>
&lt;h2 id="2-абсолютный-принцип-рефакторинга-сначала-тесты">2. Абсолютный принцип рефакторинга: &amp;ldquo;Сначала тесты&amp;rdquo;
&lt;/h2>&lt;p>Самый большой страх при изменении устаревшего кода заключается в том: &amp;ldquo;А не сломаю ли я существующее нормальное поведение (не вызову ли регрессию)?&amp;rdquo;. Единственный способ избавиться от этого страха — &amp;ldquo;автоматизированные тесты&amp;rdquo;.&lt;/p>
&lt;p>Однако в устаревшем коде тестов изначально нет. Здесь и становится важным внедрение &amp;ldquo;характеризационных тестов&amp;rdquo; (Characterization Test). Характеризационное тестирование — это тестирование, которое записывает не то, &amp;ldquo;как система должна вести себя в идеале&amp;rdquo;, а &amp;ldquo;как она ведет себя в настоящее время&amp;rdquo; в точности так, как есть.&lt;/p>
&lt;p>Следующая блок-схема показывает жизненный цикл безопасного рефакторинга.&lt;/p>
&lt;pre class="mermaid">
flowchart TD
A[&amp;#34;Определить целевой устаревший код&amp;#34;] --&amp;gt; B[&amp;#34;Написать характеризационные тесты&amp;#34;]
B --&amp;gt; C[&amp;#34;Убедиться, что все тесты пройдены&amp;#34;]
C --&amp;gt; D[&amp;#34;Выполнить небольшой рефакторинг&amp;#34;]
D --&amp;gt; E[&amp;#34;Повторно запустить тесты&amp;#34;]
E -- &amp;#34;Провал (Red)&amp;#34; --&amp;gt; F[&amp;#34;Отменить изменения (Revert)&amp;#34;]
F --&amp;gt; D
E -- &amp;#34;Успех (Green)&amp;#34; --&amp;gt; G[&amp;#34;Зафиксировать код (Commit)&amp;#34;]
G --&amp;gt; H{&amp;#34;Есть ли следующие места для улучшения?&amp;#34;}
H -- &amp;#34;Да&amp;#34; --&amp;gt; D
H -- &amp;#34;Нет&amp;#34; --&amp;gt; I[&amp;#34;Рефакторинг завершен&amp;#34;]
&lt;/pre>
&lt;p>Проходя этот цикл, разработчики всегда могут изменять код с помощью системы безопасности (страховочной сетки). Важно немедленно применять &lt;code>Revert&lt;/code> (отменить), если тест не пройден, не вдаваясь слишком глубоко в причины.&lt;/p>
&lt;hr>
&lt;h2 id="3-концепция-швов-seams-создающих-возможность-тестирования">3. Концепция &amp;ldquo;Швов&amp;rdquo; (Seams), создающих возможность тестирования
&lt;/h2>&lt;p>При попытке добавить тесты в устаревший код первая стена, с которой вы сталкиваетесь — это &amp;ldquo;зависимости&amp;rdquo;. Прямые подключения к базам данных, сетевая связь, жестко закодированный доступ к файловой системе и т. д. — если они тесно связаны, написать модульные тесты (Unit Test) невозможно.&lt;/p>
&lt;p>Здесь и появляется концепция &amp;ldquo;Шва&amp;rdquo; (Seam). Шов — это &amp;ldquo;место, где вы можете изменить поведение системы без редактирования самого кода&amp;rdquo;. В C++ в основном используются следующие три типа швов:&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Объектные швы (Object Seams)&lt;/strong>: Полиморфизм с использованием виртуальных функций (Virtual Functions).&lt;/li>
&lt;li>&lt;strong>Швы времени компиляции (Compile-time Seams)&lt;/strong>: Шаблоны (Templates) и переключение &lt;code>#include&lt;/code>.&lt;/li>
&lt;li>&lt;strong>Швы времени компоновки (Link-time Seams)&lt;/strong>: Переключение библиотек или объектных файлов для компоновки во время сборки.&lt;/li>
&lt;/ol>
&lt;p>Используя их для замены модулей производственной среды на фиктивные объекты (Mock) для тестовой среды, мы изолируем зависимости.&lt;/p>
&lt;hr>
&lt;h2 id="4-разрушение-сильной-связности-внедрение-зависимостей-dependency-injection">4. Разрушение сильной связности: Внедрение зависимостей (Dependency Injection)
&lt;/h2>&lt;p>Внедрение зависимостей (DI: Dependency Injection) — это мощный паттерн для переноса ответственности за создание объектов изнутри класса наружу.&lt;/p>
&lt;p>Во-первых, давайте посмотрим на архитектуру сильно связанного устаревшего класса C++.&lt;/p>
&lt;pre class="mermaid">
classDiagram
class LegacyOrderProcessor {
-DatabaseConnection* db
-FileLogger* logger
+LegacyOrderProcessor()
+processOrder(int orderId) void
}
class DatabaseConnection {
+DatabaseConnection()
+save(int orderId) void
}
class FileLogger {
+FileLogger()
+log(string msg) void
}
LegacyOrderProcessor --&amp;gt; DatabaseConnection : &amp;#34;Прямое создание (с использованием new)&amp;#34;
LegacyOrderProcessor --&amp;gt; FileLogger : &amp;#34;Прямое создание (с использованием new)&amp;#34;
&lt;/pre>
&lt;p>Поскольку этот &lt;code>LegacyOrderProcessor&lt;/code> напрямую вызывает &lt;code>new&lt;/code> для &lt;code>DatabaseConnection&lt;/code> и &lt;code>FileLogger&lt;/code> внутри своего конструктора, не существует шва для замены их на мок-объекты. Мы проведем рефакторинг, чтобы сделать их слабо связанными с использованием интерфейсов (чисто абстрактных классов).&lt;/p>
&lt;pre class="mermaid">
classDiagram
class IDatabase {
&amp;lt;&amp;lt;interface&amp;gt;&amp;gt;
+save(int orderId) void
}
class ILogger {
&amp;lt;&amp;lt;interface&amp;gt;&amp;gt;
+log(string msg) void
}
class ModernOrderProcessor {
-std::unique_ptr~IDatabase~ db
-std::shared_ptr~ILogger~ logger
+ModernOrderProcessor(std::unique_ptr~IDatabase~ db, std::shared_ptr~ILogger~ logger)
+processOrder(int orderId) void
}
class DatabaseConnection {
+save(int orderId) void
}
class FileLogger {
+log(string msg) void
}
IDatabase &amp;lt;|.. DatabaseConnection : &amp;#34;Реализация&amp;#34;
ILogger &amp;lt;|.. FileLogger : &amp;#34;Реализация&amp;#34;
ModernOrderProcessor --&amp;gt; IDatabase : &amp;#34;DI (Внедрение через конструктор)&amp;#34;
ModernOrderProcessor --&amp;gt; ILogger : &amp;#34;DI (Внедрение через конструктор)&amp;#34;
&lt;/pre>
&lt;h3 id="пример-устаревшего-кода-c03">Пример устаревшего кода (C++03)
&lt;/h3>&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="k">class&lt;/span> &lt;span class="nc">LegacyOrderProcessor&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">private&lt;/span>&lt;span class="o">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">DatabaseConnection&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">db_&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">FileLogger&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">logger_&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">public&lt;/span>&lt;span class="o">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">LegacyOrderProcessor&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">db_&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="n">DatabaseConnection&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;localhost&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">3306&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">logger_&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="n">FileLogger&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;/var/log/app.log&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">~&lt;/span>&lt;span class="n">LegacyOrderProcessor&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">delete&lt;/span> &lt;span class="n">db_&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">delete&lt;/span> &lt;span class="n">logger_&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">void&lt;/span> &lt;span class="nf">processOrder&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kt">int&lt;/span> &lt;span class="n">orderId&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Обработка...
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">db_&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">save&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">orderId&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">logger_&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;Order processed&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="после-рефакторинга-modern-c">После рефакторинга (Modern C++)
&lt;/h3>&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// Определение интерфейсов (Объектный шов)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="k">class&lt;/span> &lt;span class="nc">IDatabase&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">public&lt;/span>&lt;span class="o">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">virtual&lt;/span> &lt;span class="o">~&lt;/span>&lt;span class="n">IDatabase&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">default&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">virtual&lt;/span> &lt;span class="kt">void&lt;/span> &lt;span class="nf">save&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kt">int&lt;/span> &lt;span class="n">orderId&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">class&lt;/span> &lt;span class="nc">ILogger&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">public&lt;/span>&lt;span class="o">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">virtual&lt;/span> &lt;span class="o">~&lt;/span>&lt;span class="n">ILogger&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">default&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">virtual&lt;/span> &lt;span class="kt">void&lt;/span> &lt;span class="nf">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">const&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">string&lt;/span>&lt;span class="o">&amp;amp;&lt;/span> &lt;span class="n">msg&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// Архитектура, в которой зависимости внедряются извне
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="k">class&lt;/span> &lt;span class="nc">ModernOrderProcessor&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">private&lt;/span>&lt;span class="o">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">unique_ptr&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">IDatabase&lt;/span>&lt;span class="o">&amp;gt;&lt;/span> &lt;span class="n">db_&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">shared_ptr&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">ILogger&lt;/span>&lt;span class="o">&amp;gt;&lt;/span> &lt;span class="n">logger_&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">public&lt;/span>&lt;span class="o">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Внедрение через конструктор (Constructor Injection)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">ModernOrderProcessor&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">unique_ptr&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">IDatabase&lt;/span>&lt;span class="o">&amp;gt;&lt;/span> &lt;span class="n">db&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">shared_ptr&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">ILogger&lt;/span>&lt;span class="o">&amp;gt;&lt;/span> &lt;span class="n">logger&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">:&lt;/span> &lt;span class="n">db_&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">move&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">db&lt;/span>&lt;span class="p">)),&lt;/span> &lt;span class="n">logger_&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">move&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">logger&lt;/span>&lt;span class="p">))&lt;/span> &lt;span class="p">{}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">void&lt;/span> &lt;span class="nf">processOrder&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kt">int&lt;/span> &lt;span class="n">orderId&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">db_&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">save&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">orderId&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">logger_&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;Order processed&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Изменив дизайн таким образом, мы сможем легко создавать мок-объекты для &lt;code>IDatabase&lt;/code> с помощью таких фреймворков, как Google Mock (gmock), что сделает возможной разработку через тестирование (TDD).&lt;/p>
&lt;hr>
&lt;h2 id="5-демонтаж-злополучных-глобальных-переменных-и-синглтонов">5. Демонтаж злополучных глобальных переменных и синглтонов
&lt;/h2>&lt;p>Что больше всего беспокоит в устаревшем C++, так это злоупотребление глобальными переменными и &amp;ldquo;паттерном Одиночка (Singleton)&amp;rdquo;. На первый взгляд синглтон кажется удобным паттерном проектирования, но на самом деле это не более чем &amp;ldquo;глобальная переменная в шкуре объектно-ориентированного программирования&amp;rdquo;.&lt;/p>
&lt;p>Поскольку глобальное состояние является общим для тестовых случаев, оно делает параллельное выполнение тестов невозможным и вызывает нестабильные тесты (Flaky Tests) по неизвестным причинам.&lt;/p>
&lt;p>Решение состоит в том, чтобы устранить неявные зависимости от глобального состояния и явно передавать необходимые состояния в качестве аргументов функций (параметризация). Это называется &amp;ldquo;передачей контекста&amp;rdquo;.&lt;/p>
&lt;hr>
&lt;h2 id="6-модернизация-управления-памятью-и-суть-raii">6. Модернизация управления памятью и суть RAII
&lt;/h2>&lt;p>В коде эпохи C++98/03 &lt;code>new&lt;/code> и &lt;code>delete&lt;/code> разбросаны повсюду, становясь рассадником утечек памяти и висячих указателей. В Modern C++ (C++11 и новее) концепция &lt;strong>владения (Ownership)&lt;/strong> поддерживается на уровне языка, и безопасное управление ресурсами с использованием умных указателей стало стандартом.&lt;/p>
&lt;h3 id="raii-resource-acquisition-is-initialization">RAII (Resource Acquisition Is Initialization)
&lt;/h3>&lt;p>RAII — важнейшая идиома в C++. Связывая захват ресурса с инициализацией объекта (конструктор), а освобождение ресурса — с разрушением объекта (деструктор), гарантируется, что ресурсы будут гарантированно освобождены при выходе из области видимости.&lt;/p>
&lt;p>Даже при возникновении исключений (Exceptions), деструкторы локальных переменных вызываются автоматически в процессе развертывания стека (Stack Unwinding), что предотвращает утечки ресурсов.&lt;/p>
&lt;p>&lt;strong>До (опасный устаревший код)&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="kt">void&lt;/span> &lt;span class="nf">processFile&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">const&lt;/span> &lt;span class="kt">char&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">filename&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">FILE&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">file&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">fopen&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">filename&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s">&amp;#34;r&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="o">!&lt;/span>&lt;span class="n">file&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="k">return&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">Data&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">data&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="n">Data&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="o">!&lt;/span>&lt;span class="n">readData&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">file&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">data&lt;/span>&lt;span class="p">))&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">delete&lt;/span> &lt;span class="n">data&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="c1">// Легко забыть
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">fclose&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">file&lt;/span>&lt;span class="p">);&lt;/span> &lt;span class="c1">// Легко забыть
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">return&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">try&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">process&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">data&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span> &lt;span class="k">catch&lt;/span> &lt;span class="p">(...)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">delete&lt;/span> &lt;span class="n">data&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="c1">// Избегание утечки памяти при исключении
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">fclose&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">file&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">throw&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">delete&lt;/span> &lt;span class="n">data&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">fclose&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">file&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Этот код имеет чрезвычайно хрупкую структуру, требующую ручного освобождения ресурсов в каждой ветви потока управления.&lt;/p>
&lt;p>&lt;strong>После (Использование RAII и умных указателей)&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="kt">void&lt;/span> &lt;span class="nf">processFile&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">const&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">string&lt;/span>&lt;span class="o">&amp;amp;&lt;/span> &lt;span class="n">filename&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// std::ifstream управляет дескриптором файла через RAII
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">ifstream&lt;/span> &lt;span class="n">file&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">filename&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="o">!&lt;/span>&lt;span class="n">file&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">is_open&lt;/span>&lt;span class="p">())&lt;/span> &lt;span class="k">return&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// std::unique_ptr — эксклюзивный владелец, управляющий памятью кучи через RAII
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">auto&lt;/span> &lt;span class="n">data&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">make_unique&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">Data&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="o">!&lt;/span>&lt;span class="n">readData&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">file&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="o">*&lt;/span>&lt;span class="n">data&lt;/span>&lt;span class="p">))&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="c1">// Автоматически освобождается при выходе из области видимости
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Даже в случае исключения деструкторы unique_ptr и ifstream
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="c1">// надежно освободят ресурсы, поэтому это безопасно (гарантия нулевой утечки памяти)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">process&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">*&lt;/span>&lt;span class="n">data&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>С помощью этого рефакторинга объем кода значительно сократился, намерения стали ясными, и, прежде всего, была полностью гарантирована безопасность исключений (Exception Safety).&lt;/p>
&lt;hr>
&lt;h2 id="7-повышение-выразительности-с-помощью-функций-modern-c">7. Повышение выразительности с помощью функций Modern C++
&lt;/h2>&lt;p>При рефакторинге устаревшего кода следует в полной мере использовать преимущества обновлений функций языка.&lt;/p>
&lt;h3 id="71-вывод-типов-с-помощью-auto">7.1. Вывод типов с помощью &lt;code>auto&lt;/code>
&lt;/h3>&lt;p>Замена избыточных описаний, таких как длинные имена типов итераторов, на &lt;code>auto&lt;/code> улучшает читаемость. Однако лучшая практика — не использовать &lt;code>auto&lt;/code> для всего подряд, а ограничивать его случаями, когда &amp;ldquo;тип очевиден при взгляде на правую часть&amp;rdquo;.&lt;/p>
&lt;h3 id="72-вычисления-во-время-компиляции-с-помощью-constexpr-и-consteval">7.2. Вычисления во время компиляции с помощью &lt;code>constexpr&lt;/code> и &lt;code>consteval&lt;/code>
&lt;/h3>&lt;p>Мы активно используем &lt;code>constexpr&lt;/code> для уменьшения накладных расходов во время выполнения и выявления ошибок во время компиляции.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// Устаревший код (макросы и вычисления во время выполнения)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="cp">#define MAX_BUFFER_SIZE 1024
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&lt;/span>&lt;span class="k">const&lt;/span> &lt;span class="kt">double&lt;/span> &lt;span class="n">PI&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mf">3.1415926535&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kt">double&lt;/span> &lt;span class="nf">calculateCircleArea&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kt">double&lt;/span> &lt;span class="n">radius&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">PI&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">radius&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">radius&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// Стиль Modern C++ (C++20 и новее)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="k">constexpr&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">size_t&lt;/span> &lt;span class="n">MaxBufferSize&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">1024&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">constexpr&lt;/span> &lt;span class="kt">double&lt;/span> &lt;span class="n">Pi&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mf">3.14159265358979323846&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// consteval (C++20), который гарантирует, что это можно вычислить во время компиляции
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="k">consteval&lt;/span> &lt;span class="kt">double&lt;/span> &lt;span class="nf">calculateCircleArea&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kt">double&lt;/span> &lt;span class="n">radius&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">Pi&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">radius&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">radius&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// Затраты во время выполнения равны нулю. Константа-результат встраивается непосредственно в бинарный файл во время компиляции.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="k">constexpr&lt;/span> &lt;span class="kt">double&lt;/span> &lt;span class="n">area&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">calculateCircleArea&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mf">10.0&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="73-атрибут-nodiscard">7.3. Атрибут &lt;code>[[nodiscard]]&lt;/code>
&lt;/h3>&lt;p>Чтобы предотвратить ошибки, при которых игнорируется возвращаемое значение функции (особенно коды ошибок или важные состояния), добавляется атрибут &lt;code>[[nodiscard]]&lt;/code>. Это заставляет компилятор выдавать предупреждение при вызовах, которые не принимают возвращаемое значение.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="na">[[nodiscard]]&lt;/span> &lt;span class="kt">bool&lt;/span> &lt;span class="n">initializeSystem&lt;/span>&lt;span class="p">();&lt;/span> &lt;span class="c1">// Запретить игнорирование возвращаемого значения
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;hr>
&lt;h2 id="8-использование-средств-автоматизации-и-непрерывное-улучшение">8. Использование средств автоматизации и непрерывное улучшение
&lt;/h2>&lt;p>Редактировать большую базу устаревшего кода вручную нереально. Использование возможностей цепочек инструментов — кратчайший путь к успеху.&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Clang-Tidy&lt;/strong>: Мощный линтер и инструмент статического анализа для C++. Включив проверки серии &lt;code>modernize-*&lt;/code>, он автоматически применит (Fix-it) использование &lt;code>auto&lt;/code>, замену на &lt;code>nullptr&lt;/code>, добавление &lt;code>override&lt;/code> и т. д.&lt;/li>
&lt;li>&lt;strong>AddressSanitizer (ASan)&lt;/strong>: Будучи интегрированным в качестве опции компиляции (&lt;code>-fsanitize=address&lt;/code>), он точно выявляет утечки памяти и переполнения буфера во время выполнения. Он всегда должен быть включен во время выполнения тестов.&lt;/li>
&lt;li>&lt;strong>Создание пайплайна CI/CD&lt;/strong>: Используйте GitHub Actions или GitLab CI для выполнения сборок, автоматических тестов и статического анализа для всех pull-запросов, чтобы предотвратить появление нового технического долга.&lt;/li>
&lt;/ul>
&lt;hr>
&lt;h2 id="9-заключение">9. Заключение
&lt;/h2>&lt;p>Рефакторинг устаревшего кода на C++ никогда не бывает завершен в одночасье. Это тонкая и смелая операция, похожая на хирургическое вмешательство в систему.&lt;/p>
&lt;p>Пожалуйста, помните шаги, описанные в этой статье:&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Измерьте сложность и разработайте стратегию, основанную на фактах&lt;/strong>&lt;/li>
&lt;li>&lt;strong>Найдите швы и защитите систему с помощью характеризационных тестов&lt;/strong>&lt;/li>
&lt;li>&lt;strong>Разрушьте сильную связность с помощью DI и искорените глобальное состояние&lt;/strong>&lt;/li>
&lt;li>&lt;strong>Устраните беспокойство по поводу управления памятью с помощью RAII и умных указателей&lt;/strong>&lt;/li>
&lt;li>&lt;strong>Используйте функции Modern C++ и позвольте компилятору делать свою работу&lt;/strong>&lt;/li>
&lt;/ol>
&lt;p>Истинный секрет рефакторинга заключается в том, чтобы иметь дух &amp;ldquo;Правила бойскаутов (Оставь место стоянки чище, чем оно было до твоего прихода)&amp;rdquo; и продолжать улучшать код понемногу, но неуклонно, во время ежедневных задач по разработке.&lt;/p></description></item></channel></rss>