<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Блог Ильи Житенёва</title>
	<atom:link href="https://zhitenev.ru/feed/" rel="self" type="application/rss+xml" />
	<link>https://zhitenev.ru</link>
	<description>Программирование, электроника, технологии и спорт</description>
	<lastBuildDate>Tue, 15 Mar 2022 07:37:09 +0000</lastBuildDate>
	<language>ru-RU</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.7.1</generator>

<image>
	<url>https://zhitenev.ru/wp-content/uploads/2018/09/cropped-IMG_20180908_121205_1-32x32.jpg</url>
	<title>Блог Ильи Житенёва</title>
	<link>https://zhitenev.ru</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>Работа с Boost и CMake под Windows</title>
		<link>https://zhitenev.ru/rabota-s-boost-i-cmake-pod-windows/</link>
		
		<dc:creator><![CDATA[Илья]]></dc:creator>
		<pubDate>Sun, 13 Mar 2022 19:26:36 +0000</pubDate>
				<category><![CDATA[C++ и Qt]]></category>
		<category><![CDATA[boost]]></category>
		<category><![CDATA[программирование]]></category>
		<guid isPermaLink="false">https://zhitenev.ru/?p=2880</guid>

					<description><![CDATA[<p>Приветствую! Составил небольшую заметку по настройке и статической линковке библиотеки Boost с помощью CMake под Windows. Boost представляет собой набор библиотек на языке C++ с широким функционалом и является в некотором роде полигоном для апробирования некоторых идей, которые затем попадают в новые стандарты языка С++. Для установки и настройки CMake проекта прошу под кат! Установка ... <a title="Работа с Boost и CMake под Windows" class="read-more" href="https://zhitenev.ru/rabota-s-boost-i-cmake-pod-windows/" aria-label="Больше на Работа с Boost и CMake под Windows">Читать далее</a></p>
<p>The post <a href="https://zhitenev.ru/rabota-s-boost-i-cmake-pod-windows/">Работа с Boost и CMake под Windows</a> first appeared on <a href="https://zhitenev.ru">Блог Ильи Житенёва</a>.</p>]]></description>
										<content:encoded><![CDATA[<p>Приветствую! Составил небольшую заметку по настройке и статической линковке библиотеки Boost с помощью CMake под Windows. Boost представляет собой набор библиотек на языке C++ с широким функционалом и является в некотором роде полигоном для апробирования некоторых идей, которые затем попадают в новые стандарты языка С++. Для установки и настройки CMake проекта прошу под кат!</p>



<span id="more-2880"></span>



<h2 class="wp-block-heading">Установка Boost</h2>



<h3 class="wp-block-heading">1. Скачивание и подготовка библиотеки</h3>



<p>Скачайте библиотеку по адресу <a href="https://www.boost.org/users/download/" target="_blank" rel="noreferrer noopener">https://www.boost.org/users/download/</a> и распакуйте архив, например, в директорию <em>C:/Boost</em>. Затем перейдите в директорию и запустите файл <em>bootstrap.bat</em>, дождитесь окончания его работы. После этого запустите <em>b2.exe</em> и дождитесь окончания его работы. <em>b2.exe</em> осуществляет компиляцию исходного кода библиотек и у меня этот процесс занял более получаса. Точнее сказать не могу, так как оставил его работать и лёг спать, утром всё было готово. </p>



<h3 class="wp-block-heading">2. Настройка CMake</h3>



<p>Для подключения библиотеки Boost к вашему проекту добавьте следующие строки в CMakeLists.txt.</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; highlight: [14,18]; title: Пример кода с сайта https://zhitenev.ru; notranslate">
set (BOOST_ROOT &quot;C:/boost_1_78_0/&quot;) # Путь к библиотеке Boost

set (CMAKE_CXX_STANDARD 20) 
set (Boost_NO_SYSTEM_PATHS ON)
set (Boost_USE_MULTITHREADED ON)
set (Boost_USE_STATIC_LIBS ON)
set (Boost_USE_STATIC_RUNTIME OFF)
set (BOOST_ALL_DYN_LINK OFF)
 
# Подключаем необходимые модули. Для примера подключим program_options
find_package (Boost REQUIRED COMPONENTS program_options REQUIRED) 
 
# Если библиотека найдена, то
if (Boost_FOUND)
    include_directories (SYSTEM ${Boost_INCLUDE_DIR}) # подключаем заголовочные файлы
    add_executable (Boost_test &quot;main.cpp&quot;) # Создаем исполняемый файл для нашего проекта
    target_link_libraries (Boost_test ${Boost_LIBRARIES}) # подключаем библиотеку
endif ()
</pre></div>


<p>Обратите внимание на строку 16 &#8211; мы создаем исполняемый файл нашего проекта только в том случае, если библиотека найдена. Это условие не обязательно, но если вы работаете, например, в Visual Studio, то в случае отсутствия библиотеки, студия не будет отображать доступную к запуску цель, но и ошибки при компиляции не выдаст тоже. Использовать ли это условие или нет &#8211; выбор за вами.</p>



<h2 class="wp-block-heading">Boost в действии</h2>



<p>Давайте теперь для примера добавим в проект файл main.cpp со следующим кодом, взятым из примера работы с компонентом program_options библиотеки Boost:</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: cpp; title: Пример кода с сайта https://zhitenev.ru; notranslate">
#include &lt;iostream&gt;
#include &lt;boost/program_options.hpp&gt;

using namespace std;
namespace po = boost::program_options;

int main(int argc, char* argv&#x5B;])
{
    po::options_description desc(&quot;Allowed options&quot;);
    int c;
    desc.add_options()
        (&quot;help,h&quot;, &quot;produce help message&quot;)
        (&quot;compression,c&quot;, po::value&lt;int&gt;()-&gt;default_value(3), &quot;set compression level&quot;)
        ;

    po::variables_map vm;
    po::store(po::parse_command_line(argc, argv, desc), vm);
    po::notify(vm);

    if (vm.count(&quot;help&quot;)) {
        cout &lt;&lt; desc &lt;&lt; &quot;\n&quot;;
        return 1;
    }

    if (vm.count(&quot;compression&quot;)) {
        cout &lt;&lt; &quot;Compression level was set to &quot;
            &lt;&lt; vm&#x5B;&quot;compression&quot;].as&lt;int&gt;() &lt;&lt; &quot;.\n&quot;;
    }
    else {
        cout &lt;&lt; &quot;Compression level was not set.\n&quot;;
    }
	return 0;
}
</pre></div>


<p>Program_options является обработчиком параметров командной строки и предоставляет удобный интерфейс для работы. В рассмотренном фрагменте создается экземпляр объекта program_options desc и устанавливается заголовок для отображения справки. Справка будет вызываться параметром &#8211;help или -h. Далее регистрируются доступные параметры, с указанием через запятую полной и укороченной версии и описания команды. Укороченная версия параметров не является обязательной и может быть опущена. Для параметра compression устанавливается значение по умолчанию = 3. Далее идёт обработка введённых параметров. </p>



<p>П.с. из-за установленного значения по умолчанию код в строке 30 никогда не выполняется.</p>



<p>Скомпилируйте программу и запустите её передавая следующие комбинации:</p>



<ol class="wp-block-list"><li>Boost_test.exe</li><li>Boost_test.exe -c 5</li><li>Boost_test.exe -h</li><li>Boost_test.exe &#8211;help</li></ol>



<p>Справка о доступных параметров будет выглядеть так:</p>



<figure class="wp-block-image size-full"><img decoding="async" width="545" height="74" src="https://zhitenev.ru/wp-content/uploads/2022/03/image.png" alt="" class="wp-image-2883" srcset="https://zhitenev.ru/wp-content/uploads/2022/03/image.png 545w, https://zhitenev.ru/wp-content/uploads/2022/03/image-285x39.png 285w" sizes="(max-width: 545px) 100vw, 545px" /></figure><p>The post <a href="https://zhitenev.ru/rabota-s-boost-i-cmake-pod-windows/">Работа с Boost и CMake под Windows</a> first appeared on <a href="https://zhitenev.ru">Блог Ильи Житенёва</a>.</p>]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Многопоточность в Qt через наследование QThread</title>
		<link>https://zhitenev.ru/multithreading-in-qt-via-qthread-inheritance/</link>
		
		<dc:creator><![CDATA[Илья]]></dc:creator>
		<pubDate>Wed, 23 Feb 2022 15:16:34 +0000</pubDate>
				<category><![CDATA[C++ и Qt]]></category>
		<category><![CDATA[Многопоточность]]></category>
		<category><![CDATA[программирование]]></category>
		<guid isPermaLink="false">https://zhitenev.ru/?p=2873</guid>

					<description><![CDATA[<p>В одной из прошлых статей &#8220;О Многопоточности в Qt и как создать поток&#8221; рассмотрены различные способы выполнения кода в отдельном потоке при использовании фреймворка Qt. Один из способов: наследование от класса QThread и переопределение метода run() который только ленивый уже не обругал и не заклеймил. Также поступил и я но поразмыслив решил взять свои слова ... <a title="Многопоточность в Qt через наследование QThread" class="read-more" href="https://zhitenev.ru/multithreading-in-qt-via-qthread-inheritance/" aria-label="Больше на Многопоточность в Qt через наследование QThread">Читать далее</a></p>
<p>The post <a href="https://zhitenev.ru/multithreading-in-qt-via-qthread-inheritance/">Многопоточность в Qt через наследование QThread</a> first appeared on <a href="https://zhitenev.ru">Блог Ильи Житенёва</a>.</p>]]></description>
										<content:encoded><![CDATA[<p>В одной из прошлых статей &#8220;<a href="https://zhitenev.ru/about-threading-in-qt-and-how-to-create-a-thread/" target="_blank" rel="noreferrer noopener" title="О Многопоточности в Qt и как создать поток">О Многопоточности в Qt и как создать поток</a>&#8221; рассмотрены различные способы выполнения кода в отдельном потоке при использовании фреймворка Qt. Один из способов: наследование от класса QThread и переопределение метода run() который только ленивый уже не обругал и не заклеймил. Также поступил и я но поразмыслив  решил взять свои слова обратно и подробнее рассказать об этом способе и его особенностях.</p>



<span id="more-2873"></span>



<pre class="wp-block-preformatted"><strong>Содержание статьи</strong>

<a href="#1-как-это-работает" title="1. Как это работает?">1. Как это работает?</a>
<a href="#2-пример-использования" title="2. Пример использования?">2. Пример использования?</a>
<a href="#3-достоинства-и-недостатки" title="3. Достоинства и недостатки">3. Достоинства и недостатки</a></pre>



<p>В прошлой статье упоминалось, что использование метода противоречит принципам SOLID. А именно принципу открытости\закрытости (O &#8211; &#8220;open\closed principle&#8221;). Но принцип на то и принцип, что дает лишь общее абстрактное описание, не опираясь на конкретную задачу, а потому возможные некоторые вариации. Разработчику приходится самому &#8220;дозировать&#8221; те или иные принципы в своих задачах. Поэтому отбрасывать один из способов создания потока только из-за того, что один из принципов слабо реализуется, как минимум не корректно.</p>



<h2 class="wp-block-heading" id="1-как-это-работает">1. Как это работает?</h2>



<p>Если вы знакомы с языком Java, то это очень похоже на реализацию интерфейса Runnable. Создайте класс с предком QThread и переопределите метод run(). Этот метод будет запущен в отдельном потоке, а все остальные методы-члены и параметры класса, как и сам экземпляр класса продолжат своё существование в том потоке, где был аллоцирован экземпляр.</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: cpp; title: Пример кода с сайта https://zhitenev.ru; notranslate">
class ThreadMethod : public QThread {
    private:
    void run() override {
        // this code is executed in another thread
    }
};
</pre></div>


<p>Такая реализация позволяет инкапсулировать разделяемые данные (некий буфер) и саму задачу (метод исполняемый в отдельном потоке) в одном классе.</p>



<figure class="wp-block-pullquote"><blockquote><p>Только метод run() будет выполняться в отдельном потоке, все остальные методы-члены и свойства класса, а также сам экземпляр класса будут существовать в потоке, где произошло создание экземпляра класса</p></blockquote></figure>



<p>Важно помнить, что в Qt объекты существуют в том потоке, где они созданы, а при взаимодействии с некоторыми классами из других потоков может привести к неопределённому поведению (Undefined behavior). </p>



<h2 class="wp-block-heading" id="2-пример-использования">2. Пример использования?</h2>



<p>В одной из программ, разрабатываемых мною, нужен был синхронный доступ к последовательному порту. Доступ к периферии может занимать много времени, поэтому во избежание &#8220;заморозки&#8221; и подвисания основного потока объекты взаимодействующие с периферией выносятся в другие потоки. </p>



<p>Реализация класса представлена ниже</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: cpp; title: Пример кода с сайта https://zhitenev.ru; notranslate">
class DataModel : public QThread {
public:
    DataModel(QStringView port, qint32 baud, int timeout_ms = 0) 
    : m_port(port.toString()),
    m_baud(baud),
    m_timeout(timeout_ms)
    {}
    // Останавливаем выполнение потока и дожидаемся окончания
    ~DataModel() {
        stop();
        wait();
    }
    // Метод отправки данных в последовательный порт
    void send(const QByteArray&amp; msg) {
        QMutexLocker lk(&amp;m_mtx);
        m_queue.enqueue(msg);
        if(isRunning()) {
            m_sem.release();
        } else {
            start();
        }
    }
    // Метод остановки потока
    void stop() {
        QMutexLocker lk(&amp;m_mtx);
        m_stop = true;
        if(isRunning() &amp;&amp; m_sem.available() == 0) 
            m_sem.release();
    }


private:
    // метод, работающий в отдельном потоке
    void run() override {
        QScopedPointer&lt;QSerialPort&gt; port(new QSerialPort());
        port-&gt;setPortName(m_port);
        port-&gt;setBaudRate(m_baud);
        if(!port-&gt;open(QIODevice::ReadWrite) {
            emit errorOccured(QString(&quot;Can&#039;t open %1, error code %2&quot;).arg(m_port).arg(port.error()));
            return;
        }
        emit connected(true);
        do {
            m_mtx.lock();
            auto txBuffer = m_queue.dequeue();
            m_mtx.unlock();

            m_port.write(txBuffer);
            if(m_port.waitForBytesWritten(m_timeout)) {
                if(m_port.waitForReadyRead(m_timeout)) {
                    auto rxBuffer = m_port.readAll();
                    emit readyRead(rxBuffer);
                } else {
                    emit errorOccured(&quot;Read timeout&quot;);
                }
            } else {
                emit errorOccured(&quot;Can&#039;t write data&quot;);
            }

            if(m_sem.available() &gt; 0) {
                m_sem.acquire();
            } else {
                emit queueIsEmpty();
            }
            
        } while(!m_stop);
        if(m_port.isOpen())
            m_port.close();
        emit connected(false);
    }

    QQueue&lt;QByteArray&gt; m_queue;
    QSemaphore m_sem;
    QMutex m_mtx;
    bool m_stop {false};
    QString m_port;
    qint32 m_baud;
    int m_timeout;
singals:
    void readyRead(const QByteArray&amp;);
    void connected(bool);
    void errorOccured(const QString&amp;);
    void queueIsEmpty();
};
</pre></div>


<p>Работа с классом осуществляется следующим образом. Создается экземпляр класса, в конструктор которого передается имя последовательного порта, бод рейт и таймаут в мс. Если понадобиться остановить и уничтожить класс, вызывается метод <em>void stop()</em>. Новые данные передаются в качестве аргумента в метод <em>void send(const QByteArray&amp;)</em> и складываются в очередь. Если это первая попытка передачи данных, то запускается поток на выполнение, в противном случае, инкрементируем семафор. </p>



<p>При запуске потока производится подключение к последовательному порту. Если подключение не удалось, то излучаем сигнал с текстом ошибки и выходим из метода <em>void run()</em>. При завершении метода void run() поток будет завершен.</p>



<p>Далее попадаем в цикл, где берем данные из очереди и записываем их в порт и вызываем waitForBytesWritten() &#8211; это блокирующий метод, осуществляющий запись данных в порт в течение заданного таймаута (или оставьте аргумент пустым, чтобы метод ждал бесконечно до записи данных). Затем ожидаем чтения данных из порта и копируем их в <em>rxBuffer</em>. Полученные данные выводим в сигнале <em>readyRead(const QByteArray&amp;)</em>.</p>



<p>Затем проверяем есть ли необработанные состояния семафора. Если есть &#8211; обрабатываем, если нет, выдаем сигнал <em>queueIsEmpty()</em>.</p>



<h2 class="wp-block-heading" id="3-достоинства-и-недостатки">3. Достоинства и недостатки</h2>



<ul class="wp-block-list"><li>Прост в реализации, просто наследуйте класс QThread и переопределите метод <em>void run()</em>;</li><li>Позволяет инкапсулировать разделяемые данные, нет необходимости создания отдельного класса для реализации потокобезопасной структуры хранения данных.</li><li>Отсутствует обработчик событий, поэтому использование сигналов для объектов, созданных внутри метода <em>run()</em> не возможно.</li></ul>



<p>Благодарю за внимание! В следующей статье рассмотрим применение асинхронности в Qt и C++  как более высокоуровнего API для многопоточных разработок</p><p>The post <a href="https://zhitenev.ru/multithreading-in-qt-via-qthread-inheritance/">Многопоточность в Qt через наследование QThread</a> first appeared on <a href="https://zhitenev.ru">Блог Ильи Житенёва</a>.</p>]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>&#8220;Грокаем алгоритмы. Иллюстрированное пособие для программистов и любопытствующих&#8221; обзор книги</title>
		<link>https://zhitenev.ru/grokaem-algoritmy-illustrirovannoe-posobie-dlya-programmistor-i-lyubopytstvuyushih-obzor-knigi/</link>
		
		<dc:creator><![CDATA[Илья]]></dc:creator>
		<pubDate>Sun, 19 Dec 2021 12:56:09 +0000</pubDate>
				<category><![CDATA[Обзоры]]></category>
		<category><![CDATA[алгоритмы]]></category>
		<category><![CDATA[книги]]></category>
		<category><![CDATA[обучение]]></category>
		<guid isPermaLink="false">https://zhitenev.ru/?p=2849</guid>

					<description><![CDATA[<p>Приветствую!&#160;Последнее время я много читаю для развития в профессиональном плане, для закрытия пробелов в знаниях, для расширения кругозора и предметной области, для повторения забытого материала.&#160;Моя книжная полка содержит&#160;литературу,&#160;связанную&#160;с разработкой программного обеспечения, архитектурой, языком программирования Си++ и&#160;другие книги на схожую тематику. Чтобы материал лучше усваивался&#160;полезно&#160;периодически&#160;повторять и вспоминать, поэтому я решил запустить новую рубрику с обзорами книг.&#160;Это ... <a title="&#8220;Грокаем алгоритмы. Иллюстрированное пособие для программистов и любопытствующих&#8221; обзор книги" class="read-more" href="https://zhitenev.ru/grokaem-algoritmy-illustrirovannoe-posobie-dlya-programmistor-i-lyubopytstvuyushih-obzor-knigi/" aria-label="Больше на &#8220;Грокаем алгоритмы. Иллюстрированное пособие для программистов и любопытствующих&#8221; обзор книги">Читать далее</a></p>
<p>The post <a href="https://zhitenev.ru/grokaem-algoritmy-illustrirovannoe-posobie-dlya-programmistor-i-lyubopytstvuyushih-obzor-knigi/">“Грокаем алгоритмы. Иллюстрированное пособие для программистов и любопытствующих” обзор книги</a> first appeared on <a href="https://zhitenev.ru">Блог Ильи Житенёва</a>.</p>]]></description>
										<content:encoded><![CDATA[<p>Приветствую!&nbsp;Последнее время я много читаю для развития в профессиональном плане, для закрытия пробелов в знаниях, для расширения кругозора и предметной области, для повторения забытого материала.&nbsp;Моя книжная полка содержит&nbsp;литературу,&nbsp;связанную&nbsp;с разработкой программного обеспечения, архитектурой, языком программирования Си++ и&nbsp;другие книги на схожую тематику. Чтобы материал лучше усваивался&nbsp;полезно&nbsp;периодически&nbsp;повторять и вспоминать, поэтому я решил запустить новую рубрику с обзорами книг.&nbsp;Это позволит мне ещё раз вспомнить и прокрутить в голове содержание книги и поделиться&nbsp;мнением о книге, стоит ли её приобретать или это информационный мусор с некачественным переводом, ляпами, опечатками&nbsp;и ужасным оформлением,&nbsp;выпущенная горе-издателем с целью лишь бы что-то продать. Профессиональная литература стоит не дешево, но качество выпускаемых книг очень разное, поэтому если&nbsp;вы думаете стоит ли приобретать и изучать ту или иную книгу или нет, то&nbsp;эта рубрика&nbsp;будет&nbsp;кстати. Я и сам часто читаю отзывы на книгу перед покупкой и буду рад, если вы поделитесь отзывом&nbsp;на другие книги&nbsp;схожей тематики.&nbsp;</p>



<p>Первой книгой для обзора стала книга Адитьи Бхаргавы “Грокаем алгоритмы. Иллюстрированное пособие для программистов и любопытствующих” издательства “Питер”. </p>



<span id="more-2849"></span>



<p>Каждый кто впервые видел название книги обращал внимание на незнакомое и непонятное слово “грокаем”. Что же означает “грокать”? Ответ на этот вопрос приведен там же, прямо на обложке книги в виде цитаты:</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow"><p>&#8220;Грокнуть” означает понять так полно, что наблюдатель становится частью объекта наблюдения&#8230;</p><cite>Р. Хайнлайн &#8211; американский писатель-фантаст&nbsp;</cite></blockquote>



<pre class="wp-block-preformatted"><strong>Содержание статьи</strong>

<a href="#1. Содержание" title="#1. Содержание">1. Содержание</a> 
<a href="#2. Изложение материала" title="#2. Изложение материала">2. Изложение материала </a>
<a href="#3. Примеры и аналогии из жизни" title="#3. Примеры и аналогии из жизни">3. Примеры и аналогии из жизни </a>
<a href="#4. Оформление, печать, общее впечатление" title="#4. Оформление, печать, общее впечатление">4. Оформление, печать, общее впечатление</a>
<a href="#5. Итоговая оценка и заключение" title="#5. Итоговая оценка и заключение">5. Итоговая оценка и заключение</a></pre>



<div class="wp-block-image"><figure class="aligncenter size-large"><img fetchpriority="high" decoding="async" width="900" height="1200" src="https://zhitenev.ru/wp-content/uploads/2021/12/IMG_20211217_230459-900x1200.jpg" alt="Адитья Бхаргава “Грокаем алгоритмы. Иллюстрированное пособие для программистов и любопытствующих”" class="wp-image-2860" srcset="https://zhitenev.ru/wp-content/uploads/2021/12/IMG_20211217_230459-900x1200.jpg 900w, https://zhitenev.ru/wp-content/uploads/2021/12/IMG_20211217_230459-576x768.jpg 576w, https://zhitenev.ru/wp-content/uploads/2021/12/IMG_20211217_230459-285x380.jpg 285w, https://zhitenev.ru/wp-content/uploads/2021/12/IMG_20211217_230459-768x1024.jpg 768w, https://zhitenev.ru/wp-content/uploads/2021/12/IMG_20211217_230459-1152x1536.jpg 1152w, https://zhitenev.ru/wp-content/uploads/2021/12/IMG_20211217_230459.jpg 1500w" sizes="(max-width: 900px) 100vw, 900px" /><figcaption>Обложка книги</figcaption></figure></div>



<h2 class="wp-block-heading" id="1. Содержание">1. Содержание</h2>



<p>Книга содержит описание широкого круга базовых алгоритмов и структур данных.&nbsp;Первые главы знакомят с понятием бинарного поиска и оценкой худшего времени выполнения алгоритма, известного как “О-большое”. Далее следуют несколько глав о сортировках&nbsp;с картинками и примерами, другие&nbsp;несколько глав посвящены структурам данных.&nbsp;Для каждой структуры приводится оценка&nbsp;времени&nbsp;выполнений базовых операций&nbsp;и аналогия&nbsp;из&nbsp;жизни. Этот трюк позволяет не учить значения “О-большое”, а поняв аналогию всегда легко и быстро самостоятельно воспроизводить этот параметр.&nbsp;</p>



<p>Не обходится повествование без хэш-таблиц и графов.&nbsp;Приводится описание и реализация алгоритмов поиска в ширину и в глубину.&nbsp;&nbsp;</p>



<p>Одна из глав посвящена жадным алгоритмам, другая динамическому программированию. Предпоследнюю&nbsp;главу&nbsp;я бы назвал поверхностным знакомством с машинным обучением. Читателя знакомят с двумя простейшими алгоритмами для построения рекомендательной системы.&nbsp;&nbsp;</p>



<figure class="wp-block-gallery aligncenter columns-3 is-cropped wp-block-gallery-1 is-layout-flex wp-block-gallery-is-layout-flex"><ul class="blocks-gallery-grid"><li class="blocks-gallery-item"><figure><a href="https://zhitenev.ru/wp-content/uploads/2021/12/IMG_20211217_230624.jpg"><img decoding="async" width="576" height="768" src="https://zhitenev.ru/wp-content/uploads/2021/12/IMG_20211217_230624-576x768.jpg" alt="" data-id="2854" data-full-url="https://zhitenev.ru/wp-content/uploads/2021/12/IMG_20211217_230624.jpg" data-link="https://zhitenev.ru/wp-content/uploads/2021/12/IMG_20211217_230624.jpg" class="wp-image-2854" srcset="https://zhitenev.ru/wp-content/uploads/2021/12/IMG_20211217_230624-576x768.jpg 576w, https://zhitenev.ru/wp-content/uploads/2021/12/IMG_20211217_230624-900x1200.jpg 900w, https://zhitenev.ru/wp-content/uploads/2021/12/IMG_20211217_230624-285x380.jpg 285w, https://zhitenev.ru/wp-content/uploads/2021/12/IMG_20211217_230624-768x1024.jpg 768w, https://zhitenev.ru/wp-content/uploads/2021/12/IMG_20211217_230624-1152x1536.jpg 1152w, https://zhitenev.ru/wp-content/uploads/2021/12/IMG_20211217_230624.jpg 1500w" sizes="(max-width: 576px) 100vw, 576px" /></a></figure></li><li class="blocks-gallery-item"><figure><a href="https://zhitenev.ru/wp-content/uploads/2021/12/IMG_20211217_230639.jpg"><img loading="lazy" decoding="async" width="576" height="768" src="https://zhitenev.ru/wp-content/uploads/2021/12/IMG_20211217_230639-576x768.jpg" alt="" data-id="2855" data-full-url="https://zhitenev.ru/wp-content/uploads/2021/12/IMG_20211217_230639.jpg" data-link="https://zhitenev.ru/wp-content/uploads/2021/12/IMG_20211217_230639.jpg" class="wp-image-2855" srcset="https://zhitenev.ru/wp-content/uploads/2021/12/IMG_20211217_230639-576x768.jpg 576w, https://zhitenev.ru/wp-content/uploads/2021/12/IMG_20211217_230639-900x1200.jpg 900w, https://zhitenev.ru/wp-content/uploads/2021/12/IMG_20211217_230639-285x380.jpg 285w, https://zhitenev.ru/wp-content/uploads/2021/12/IMG_20211217_230639-768x1024.jpg 768w, https://zhitenev.ru/wp-content/uploads/2021/12/IMG_20211217_230639-1152x1536.jpg 1152w, https://zhitenev.ru/wp-content/uploads/2021/12/IMG_20211217_230639.jpg 1500w" sizes="auto, (max-width: 576px) 100vw, 576px" /></a></figure></li><li class="blocks-gallery-item"><figure><a href="https://zhitenev.ru/wp-content/uploads/2021/12/IMG_20211217_230642.jpg"><img loading="lazy" decoding="async" width="576" height="768" src="https://zhitenev.ru/wp-content/uploads/2021/12/IMG_20211217_230642-576x768.jpg" alt="" data-id="2856" data-full-url="https://zhitenev.ru/wp-content/uploads/2021/12/IMG_20211217_230642.jpg" data-link="https://zhitenev.ru/wp-content/uploads/2021/12/IMG_20211217_230642.jpg" class="wp-image-2856" srcset="https://zhitenev.ru/wp-content/uploads/2021/12/IMG_20211217_230642-576x768.jpg 576w, https://zhitenev.ru/wp-content/uploads/2021/12/IMG_20211217_230642-900x1200.jpg 900w, https://zhitenev.ru/wp-content/uploads/2021/12/IMG_20211217_230642-285x380.jpg 285w, https://zhitenev.ru/wp-content/uploads/2021/12/IMG_20211217_230642-768x1024.jpg 768w, https://zhitenev.ru/wp-content/uploads/2021/12/IMG_20211217_230642-1152x1536.jpg 1152w, https://zhitenev.ru/wp-content/uploads/2021/12/IMG_20211217_230642.jpg 1500w" sizes="auto, (max-width: 576px) 100vw, 576px" /></a></figure></li><li class="blocks-gallery-item"><figure><a href="https://zhitenev.ru/wp-content/uploads/2021/12/IMG_20211217_230653.jpg"><img loading="lazy" decoding="async" width="576" height="768" src="https://zhitenev.ru/wp-content/uploads/2021/12/IMG_20211217_230653-576x768.jpg" alt="" data-id="2857" data-full-url="https://zhitenev.ru/wp-content/uploads/2021/12/IMG_20211217_230653.jpg" data-link="https://zhitenev.ru/wp-content/uploads/2021/12/IMG_20211217_230653.jpg" class="wp-image-2857" srcset="https://zhitenev.ru/wp-content/uploads/2021/12/IMG_20211217_230653-576x768.jpg 576w, https://zhitenev.ru/wp-content/uploads/2021/12/IMG_20211217_230653-900x1200.jpg 900w, https://zhitenev.ru/wp-content/uploads/2021/12/IMG_20211217_230653-285x380.jpg 285w, https://zhitenev.ru/wp-content/uploads/2021/12/IMG_20211217_230653-768x1024.jpg 768w, https://zhitenev.ru/wp-content/uploads/2021/12/IMG_20211217_230653-1152x1536.jpg 1152w, https://zhitenev.ru/wp-content/uploads/2021/12/IMG_20211217_230653.jpg 1500w" sizes="auto, (max-width: 576px) 100vw, 576px" /></a></figure></li><li class="blocks-gallery-item"><figure><a href="https://zhitenev.ru/wp-content/uploads/2021/12/IMG_20211217_230656.jpg"><img loading="lazy" decoding="async" width="576" height="768" src="https://zhitenev.ru/wp-content/uploads/2021/12/IMG_20211217_230656-576x768.jpg" alt="" data-id="2858" data-full-url="https://zhitenev.ru/wp-content/uploads/2021/12/IMG_20211217_230656.jpg" data-link="https://zhitenev.ru/wp-content/uploads/2021/12/IMG_20211217_230656.jpg" class="wp-image-2858" srcset="https://zhitenev.ru/wp-content/uploads/2021/12/IMG_20211217_230656-576x768.jpg 576w, https://zhitenev.ru/wp-content/uploads/2021/12/IMG_20211217_230656-900x1200.jpg 900w, https://zhitenev.ru/wp-content/uploads/2021/12/IMG_20211217_230656-285x380.jpg 285w, https://zhitenev.ru/wp-content/uploads/2021/12/IMG_20211217_230656-768x1024.jpg 768w, https://zhitenev.ru/wp-content/uploads/2021/12/IMG_20211217_230656-1152x1536.jpg 1152w, https://zhitenev.ru/wp-content/uploads/2021/12/IMG_20211217_230656.jpg 1500w" sizes="auto, (max-width: 576px) 100vw, 576px" /></a></figure></li></ul><figcaption class="blocks-gallery-caption">Оглавление</figcaption></figure>



<p>Последняя глава&nbsp;знакомит с&nbsp;кратким&nbsp;изложением&nbsp;других&nbsp;широко распространённых алгоритмов:&nbsp;SHA, фильтр Блума,&nbsp;MapReduce, преобразование Фурье и другие.&nbsp;В конце представлен&nbsp;список алгоритмов для дальнейшего самостоятельного изучения. В целом эта глава подсказывает заинтересованному читателю куда можно двинуться далее, после прочтения книги.&nbsp;</p>



<h2 class="wp-block-heading" id="2. Изложение материала">2. Изложение материала</h2>



<p>В книге множество простых и минималистичных иллюстраций и пояснений, многие построены на житейских аналогиях. Для некоторых алгоритмов приведены примеры реализации&nbsp;на языке Python. Изложение идёт последовательно от первой главы до последней, часто на основе предыдущей главы строится введение в следующую.&nbsp;&nbsp;</p>



<figure class="wp-block-gallery aligncenter columns-2 is-cropped wp-block-gallery-2 is-layout-flex wp-block-gallery-is-layout-flex"><ul class="blocks-gallery-grid"><li class="blocks-gallery-item"><figure><a href="https://zhitenev.ru/wp-content/uploads/2021/12/IMG_20211217_230732-1.jpg"><img loading="lazy" decoding="async" width="792" height="735" src="https://zhitenev.ru/wp-content/uploads/2021/12/IMG_20211217_230732-1.jpg" alt="" data-id="2853" class="wp-image-2853" srcset="https://zhitenev.ru/wp-content/uploads/2021/12/IMG_20211217_230732-1.jpg 792w, https://zhitenev.ru/wp-content/uploads/2021/12/IMG_20211217_230732-1-730x677.jpg 730w, https://zhitenev.ru/wp-content/uploads/2021/12/IMG_20211217_230732-1-285x264.jpg 285w, https://zhitenev.ru/wp-content/uploads/2021/12/IMG_20211217_230732-1-768x713.jpg 768w" sizes="auto, (max-width: 792px) 100vw, 792px" /></a><figcaption class="blocks-gallery-item__caption">Иллюстрация к пояснению алгоритма &#8220;к-ближайших соседей</figcaption></figure></li><li class="blocks-gallery-item"><figure><a href="https://zhitenev.ru/wp-content/uploads/2021/12/IMG_20211217_230613.jpg"><img loading="lazy" decoding="async" width="1500" height="2000" src="https://zhitenev.ru/wp-content/uploads/2021/12/IMG_20211217_230613.jpg" alt="" data-id="2859" data-full-url="https://zhitenev.ru/wp-content/uploads/2021/12/IMG_20211217_230613.jpg" data-link="https://zhitenev.ru/wp-content/uploads/2021/12/IMG_20211217_230613.jpg" class="wp-image-2859" srcset="https://zhitenev.ru/wp-content/uploads/2021/12/IMG_20211217_230613.jpg 1500w, https://zhitenev.ru/wp-content/uploads/2021/12/IMG_20211217_230613-576x768.jpg 576w, https://zhitenev.ru/wp-content/uploads/2021/12/IMG_20211217_230613-900x1200.jpg 900w, https://zhitenev.ru/wp-content/uploads/2021/12/IMG_20211217_230613-285x380.jpg 285w, https://zhitenev.ru/wp-content/uploads/2021/12/IMG_20211217_230613-768x1024.jpg 768w, https://zhitenev.ru/wp-content/uploads/2021/12/IMG_20211217_230613-1152x1536.jpg 1152w" sizes="auto, (max-width: 1500px) 100vw, 1500px" /></a><figcaption class="blocks-gallery-item__caption">Иллюстрация к алгоритму быстрой сортировки</figcaption></figure></li></ul><figcaption class="blocks-gallery-caption">Иллюстрации к алгоритмам из книги</figcaption></figure>



<p>Материал подается&nbsp;простым доступным языком, читается&nbsp;очень легко, как говорится “взахлеб”,&nbsp;начав чтение&nbsp;остановиться&nbsp;уже невозможно.&nbsp;Я так пару раз свою станцию метро проезжал.&nbsp;Объяснение построено&nbsp;последовательно, поэтому книга подойдет&nbsp;даже тем, кто не имеет абсолютно никакого опыта,&nbsp;можно&nbsp;смело&nbsp;давать и детям, интересующимся программированием.&nbsp;&nbsp;</p>



<p>Единственный нюанс на мой взгляд,&nbsp;<em>мне&nbsp;не&nbsp;понравилось разъяснение динамического программирования</em>. Пришлось по нескольку раз перечитать, обратиться к внешним источникам, проделать самостоятельно, не подглядывая в книгу все примеры, и&nbsp;тогда в голове более-менее сложился&nbsp;пазл-представление о том, как работает динамическое программирование.&nbsp;</p>



<h2 class="wp-block-heading" id="3. Примеры и аналогии из жизни">3. Примеры и аналогии из жизни</h2>



<p>В качестве жизненной аналогии очень понравилась оценка сложности простейших операций с массивом на примере библиотеки. Привожу пример из книги в вольном пересказе:&nbsp;</p>



<p>Представьте библиотеку, большую библиотеку, и перед вами стоит две задачи: найти книгу в библиотеке и добавить новую книгу. Конечно же, в этой задачи книги хранятся в отсортированном виде. Если&nbsp;известен&nbsp;индекс&nbsp;книги (её адрес: № шкафа, № полки и позицию книги на полке), например&nbsp;подсказал библиотекарь,&nbsp;где лежит эта книга, то можно&nbsp;найти книгу очень быстро &#8211; просто прийти и взять её.&nbsp;В этом случае&nbsp;книга найдена&nbsp;за константное время&nbsp;O(1).&nbsp;Если индекса нет, то придётся&nbsp;потратить побольше времени.&nbsp;Можно&nbsp;пойти к полке в центре зала, посмотреть на какую букву там книги и понять в какой половине зала вам искать&nbsp;эту&nbsp;книгу. Затем пойти к среднему шкафу в оставшейся половине зала и повторить операцию. Это&nbsp;алгоритм&nbsp;бинарного поиска и его&nbsp;сложность по времени пропорциональна&nbsp;O(lg(n)), где n &#8211; количество книг в библиотеке.&nbsp;&nbsp;</p>



<p>Переходим к ситуации, когда в библиотеку поступила новая книга и надо поставить её на нужное место. Тут и начинается самое сложное&nbsp;и интересное.&nbsp;Найти место можно бинарным поиском,&nbsp;но&nbsp;чтобы поставить&nbsp;книгу&nbsp;в шкаф, необходимо все последующие книги сдвинуть на одно место&nbsp;вправо! Не только полку, а весь шкаф и все полки во всех последующих шкафах!&nbsp;Худшим случаем будет ситуация, когда книга должна заняться самое первое место, а значит все n-книг в библиотеке необходимо будет&nbsp;передвинуть. Сложность&nbsp;этой операции в худшем случае равна O(n).&nbsp;</p>



<h2 class="wp-block-heading" id="4. Оформление, печать, общее впечатление">4. Оформление, печать, общее впечатление</h2>



<p>Общее впечатление о книге&nbsp;оцениваю&nbsp;на отлично! Оформление выполнено качественно, опечаток не припомню, откровенных ляпов и кривого перевода тоже. Бумага плотная, белая, приятная на ощупь, контрастная печать, приятный шрифт. Издательство не подкачало, отработали качественно!&nbsp;&nbsp;</p>



<h2 class="wp-block-heading" id="5. Итоговая оценка и заключение">5. Итоговая оценка и заключение</h2>



<p>Итак, подводя итоги,&nbsp;я решил&nbsp;оценивать, эту и последующие в этой рубрике&nbsp;книги,&nbsp;по следующим параметрам:&nbsp;</p>



<ul class="wp-block-list"><li>Содержание &#8211; <strong>5</strong>&nbsp;&nbsp;</li><li>Изложение материала &#8211; <strong>5</strong>&nbsp;</li><li>Примеры, пояснения и аналогии &#8211; <strong>4</strong>&nbsp;</li><li>Иллюстрации и примеры кода &#8211; <strong>5</strong>&nbsp;</li><li>Качество печати, перевода, бумаги, отсутствие опечаток и ляпов &#8211; <strong>5</strong>&nbsp;</li><li>Порекомендовал бы я её кому-то ещё? &#8211; <strong>5</strong>&nbsp;</li></ul>



<p>Итоговая оценка книги &#8220;Грокаем алгоритмы&#8221;: <strong>4,83</strong>.</p>



<p>А вы читали&nbsp;Грокаем&nbsp;алгоритмы? Поделитесь мнением в комментариях.&nbsp;</p>



<p>В следующий раз на обзор попадет книга Скотта Майерса “Эффективное программирование C++. 42 рекомендации по использованию С++11 и С++14”&nbsp;</p>



<div class="wp-block-image"><figure class="aligncenter size-thumbnail is-resized"><img loading="lazy" decoding="async" src="https://zhitenev.ru/wp-content/uploads/2021/12/IMG_20211219_142354-285x380.jpg" alt="Книга Скотта Майерса “Эффективное программирование C++. 42 рекомендации по использованию С++11 и С++14”" class="wp-image-2850" width="285" height="380" title="Книга Скотта Майерса “Эффективное программирование C++. 42 рекомендации по использованию С++11 и С++14”" srcset="https://zhitenev.ru/wp-content/uploads/2021/12/IMG_20211219_142354-285x380.jpg 285w, https://zhitenev.ru/wp-content/uploads/2021/12/IMG_20211219_142354-576x768.jpg 576w, https://zhitenev.ru/wp-content/uploads/2021/12/IMG_20211219_142354-900x1200.jpg 900w, https://zhitenev.ru/wp-content/uploads/2021/12/IMG_20211219_142354-768x1024.jpg 768w, https://zhitenev.ru/wp-content/uploads/2021/12/IMG_20211219_142354-1152x1536.jpg 1152w, https://zhitenev.ru/wp-content/uploads/2021/12/IMG_20211219_142354.jpg 1500w" sizes="auto, (max-width: 285px) 100vw, 285px" /><figcaption>Книга Скотта Майерса “Эффективное программирование C++. 42 рекомендации по использованию С++11 и С++14” </figcaption></figure></div>



<p>Удачи!</p><p>The post <a href="https://zhitenev.ru/grokaem-algoritmy-illustrirovannoe-posobie-dlya-programmistor-i-lyubopytstvuyushih-obzor-knigi/">“Грокаем алгоритмы. Иллюстрированное пособие для программистов и любопытствующих” обзор книги</a> first appeared on <a href="https://zhitenev.ru">Блог Ильи Житенёва</a>.</p>]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Отлов событий в Qt с помощью фильтров событий</title>
		<link>https://zhitenev.ru/otlov-sobytiy-v-qt-s-pomoshiyu-filtrov-sobytiy/</link>
		
		<dc:creator><![CDATA[Илья]]></dc:creator>
		<pubDate>Fri, 05 Nov 2021 17:41:59 +0000</pubDate>
				<category><![CDATA[C++ и Qt]]></category>
		<category><![CDATA[C++]]></category>
		<category><![CDATA[Events]]></category>
		<category><![CDATA[Qt]]></category>
		<category><![CDATA[программирование]]></category>
		<guid isPermaLink="false">https://zhitenev.ru/?p=2841</guid>

					<description><![CDATA[<p>Исполнение компьютерных программ происходит последовательно, выполняя одну инструкцию за другой. Иногда в программе требуется дождаться некоторого события и отреагировать на него. Ждать этого события в бесконечном цикле расточительно и для обработки таких событий придуманы библиотеки и системные функции, позволяющие вызывать пользовательские функции в тот момент когда происходит событие. В Qt имеется собственная система событий. Очевидный ... <a title="Отлов событий в Qt с помощью фильтров событий" class="read-more" href="https://zhitenev.ru/otlov-sobytiy-v-qt-s-pomoshiyu-filtrov-sobytiy/" aria-label="Больше на Отлов событий в Qt с помощью фильтров событий">Читать далее</a></p>
<p>The post <a href="https://zhitenev.ru/otlov-sobytiy-v-qt-s-pomoshiyu-filtrov-sobytiy/">Отлов событий в Qt с помощью фильтров событий</a> first appeared on <a href="https://zhitenev.ru">Блог Ильи Житенёва</a>.</p>]]></description>
										<content:encoded><![CDATA[<p>Исполнение компьютерных программ происходит последовательно, выполняя одну инструкцию за другой. Иногда в программе требуется дождаться некоторого события и отреагировать на него. Ждать этого события в бесконечном цикле расточительно и для обработки таких событий придуманы библиотеки и системные функции, позволяющие вызывать пользовательские функции в тот момент когда происходит событие. В Qt имеется собственная система событий. Очевидный и топорный способ реакции на возникшее событие — переопределение методов базового класса, но этот же способ является и самым громоздким, а также может приводить к нарушению принципов DRY (don’t repeat yourself) т. е. дублированию кода. В этой статье вы познакомитесь со способом вынесения пользовательских обработчиков событий в отдельный класс с возможностью повторного использования.</p>



<span id="more-2841"></span>



<pre class="wp-block-preformatted"><strong>Содержание статьи</strong>

<a href="#1. Создание главного окна" title="#1. Создание главного окна">1. Создание главного окна</a>
<a href="#2. Фильтры событий" title="#2. Фильтры событий">2. Фильтры событий</a>
<a href="#2.1 Определение класса фильтра событий" title="#2.1 Определение класса фильтра событий">2.1 Определение класса фильтра событий</a>
<a href="#2.2 Реализация фильтра событий" title="#2.2 Реализация фильтра событий">2.2 Реализация фильтра событий</a>
<a href="#2.3 Обработчик события нажатия кнопки" title="#2.3 Обработчик события нажатия кнопки">2.3 Обработчик события нажатия кнопки</a>
<a href="#2.4 Обработчик события отпускания кнопки" title="#2.4 Обработчик события отпускания кнопки">2.4 Обработчик события отпускания кнопки</a>
<a href="#2.5 Обработчик события перемещения мыши" title="#2.5 Обработчик события перемещения мыши">2.5 Обработчик события перемещения мыши</a>
<a href="#3. Выводы" title="#3. Выводы">3. Выводы</a>
<a href="#4. Исходный код" title="#4. Исходный код">4. Исходный код</a></pre>



<p>В учебных целях давайте сделаем программу для рисования отрезков. Нажатием левой кнопки мыши будет указываться начало отрезка, а точка отпускания кнопки мыши — будет точкой окончания отрезка.</p>



<p>Алгоритм следующий</p>



<ol class="wp-block-list"><li>При нажатии левой кнопки мыши сохраняем точку в которой произошло нажатие;</li><li>При отпускании кнопки мыши строим отрезок из сохраненной точки в текущую точку.Для улучшения пользовательского интерфейса давайте усложним задачу добавив отображение вспомогательного отрезка из точки нажатия в точку, где расположен указатель мыши, чтобы пользователь видел как будет проложен рисуемый отрезок. В этом случае к алгоритму добавится ещё третий пункт;</li><li>При передвижении мыши рисуем отрезок из точки нажатия до текущей позиции курсора и сохраняем указатель на объект рисования, предварительно, удалив из сцены предыдущий сохраненный объект, если таковой имеется.</li></ol>



<h2 class="wp-block-heading" id="1. Создание главного окна">1. Создание главное окна</h2>



<p>Создадим файл main.cpp следующего содержания (см. листинг 1)</p>


<div class="wp-block-syntaxhighlighter-code aligncenter"><pre class="brush: cpp; title: Пример кода с сайта https://zhitenev.ru; notranslate">
#include &lt;QApplication&gt;
#include &lt;QtWidgets&gt;
#include &quot;PaintEvent.h&quot;

int main(int argc, char *argv&#x5B;])
{
    QApplication a(argc, argv);

    auto view = new QGraphicsView();
    view-&gt;resize(800,600);

    auto scene = new QGraphicsScene(view);
    view-&gt;setScene(scene);

    auto paintEvent = new PaintEvent(scene);
    scene-&gt;installEventFilter(paintEvent);

    view-&gt;show();

    return a.exec();
}
</pre></div>


<p class="has-text-align-center">Листинг 1. Код файла main.cpp</p>



<p>Рисование в Qt осуществляется в объекте QGraphicsScene, указатель на который передается для отображения в виджет QGraphicsView. Связка QGraphicsScene и QGraphicsView работает в парадигме модель-представление.</p>



<p>В приведенном выше исходном коде создается экземпляр класса QGraphicsView и устанавливается размер 800х600 пикселей. Создается экземпляр QGraphicsScene с указанием предка — экземпляра класса QGraphicsView. Экземпляру класса QGraphicsView устанавливаем сцену для отображения. Создаем фильтр событий — экземпляр пользовательского класса (подробно разберем далее) PaintEvent, передавая в конструктор указатель на сцену. Методом installEventFilter устанавливаем фильтр событий paintEvent для объекта scene. И делаем экземпляр QGraphicsView видимым.</p>



<p>При попытке запустить приложение увидим окно программы как на рисунке 1.</p>



<div class="wp-block-image"><figure class="aligncenter size-full"><img loading="lazy" decoding="async" width="802" height="632" src="https://zhitenev.ru/wp-content/uploads/2021/11/image.png" alt="Рисунок 1.  Главное окно программы" class="wp-image-2843" srcset="https://zhitenev.ru/wp-content/uploads/2021/11/image.png 802w, https://zhitenev.ru/wp-content/uploads/2021/11/image-730x575.png 730w, https://zhitenev.ru/wp-content/uploads/2021/11/image-285x225.png 285w, https://zhitenev.ru/wp-content/uploads/2021/11/image-768x605.png 768w" sizes="auto, (max-width: 802px) 100vw, 802px" /><figcaption>Рисунок 1.  Главное окно программы</figcaption></figure></div>



<p>Можете поводить мышью, по нажимать, но ничего не произойдёт. Необходимо привязаться к некоторым событиям и описать реакцию программы на эти действия.</p>



<p>Очевидный и одновременно громоздкий способ реализации алгоритма это создать объект, унаследованный от класса QGraphicsScene и в собственном классе переопределить методы:</p>



<ul class="wp-block-list"><li>virtual void mousePressEvent(QGraphicsSceneMouseEvent *mouseEvent);</li><li>virtual void mouseReleaseEvent(QGraphicsSceneMouseEvent *mouseEvent);</li><li>virtual void mouseMoveEvent(QGraphicsSceneMouseEvent *mouseEvent);</li></ul>



<p>Это не лучший способ, к тому же он способен утяжелить и раздуть код. Аккуратно отловить события можно фильтром событий.</p>



<h2 class="wp-block-heading" id="2. Фильтры событий">2. Фильтры событий</h2>



<p>Фильтры событий это объекты унаследованные от QObject и имеющие метод bool eventFilter(QObject* object, QEvent* event); Метод принимает 2 параметра: указатель QObject на объект, событие для которого было перехвачено и указатель на QEvent – объект, описывающий произошедшее событие. Возвращает метод значение булева типа. Это свойство я называю поглощением события. Поглощение события не дает событию выйти за пределы фильтра, оно поглощается. При возвращении значения true событие перехватывается фильтром и не будет передано далее к стандартным обработчикам — фильтр поглощает событие, при возвращении false событие будет передано дальше — фильтр прозрачен.</p>



<h3 class="wp-block-heading" id="2.1 Определение класса фильтра событий">2.1 Определение класса фильтра событий</h3>



<p>Создадим определение класса PaintEvent в файле painEvent.cpp (см. листинг 2). Разместим в закрытой секции указатели на экземпляры следующих классов:</p>



<ul class="wp-block-list"><li>QGraphicsScene – отвечает за рисование объектов;</li><li>QGraphicsItem – хранит последний отрезок, созданный при перемещении курсора;</li><li>QPointF – хранит позицию курсора мыши в момент нажатия кнопки;</li><li>два экземпляра QPen для задания стиля рисуемых отрезков: один для вспомогательного и один для постоянных отрезков,</li></ul>



<p>и булеву переменную pressed – хранящую состояние длящегося нажатия.</p>



<p>Конструктор принимает указатель на объект сцены класса QGraphicsScene. Сцена будет использоваться для рисования и будет родителем для PaintEvent. Метод bool eventFilter(QObject* object, QEvent* event) будет исполнять роль фильтра событий.</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: cpp; title: Пример кода с сайта https://zhitenev.ru; notranslate">
#pragma once

#include &lt;QObject&gt;
#include &lt;QtWidgets&gt;

class PaintEvent : public QObject
{
    Q_OBJECT
    QGraphicsScene* scene {nullptr};
    QPointF startPoint;
    QGraphicsItem* draftLine {nullptr};
    QPen tempPen;
    QPen pen;
    bool pressed = false;
public:
    explicit PaintEvent(QGraphicsScene* inScene);
    bool eventFilter(QObject* object, QEvent* event);
};
</pre></div>


<p class="has-text-align-center">Листинг 2. Определение класса PaintEvent в файле paintEvent.h</p>



<h3 class="wp-block-heading" id="2.2 Реализация фильтра событий">2.2 Реализация фильтра событий</h3>



<p>Приступим к реализации методов класса PaintEvent. Их два: конструктор и eventFilter.</p>



<p>В конструкторе инициализируем параметр scene указателем inScene и настроим экземпляры класса QPen описывающие стили пера для рисования отрезков: pen и tempPen. Перу pen установим цвет Qt::blue методом setColor и толщину 2 пикслея методом setWidth. Аналогично для пера tempPen установим цвет Qt::gray и толщину 1 пиксель. Pen определяет стиль нарисованных отрезков, появляющиеся после отпускания кнопки мыши. TempPen задает стиль для вспомогательных отрезков, рисуемых из позиции курсора в момент нажатия и до текущего положения мыши, пока кнопка мыши ещё нажата. (см. листинг 3)</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: cpp; title: Пример кода с сайта https://zhitenev.ru; notranslate">
#include &quot;PaintEvent.h&quot;

#include &lt;QGraphicsSceneEvent&gt;
#include &lt;QPainter&gt;
#include &lt;QDebug&gt;

PaintEvent::PaintEvent(QGraphicsScene* inScene) :
    QObject(inScene),
    scene(inScene)
{
    tempPen.setColor(Qt::gray);
    tempPen.setWidth(1);
    pen.setColor(Qt::blue);
    pen.setWidth(2);
}
</pre></div>


<p class="has-text-align-center">Листинг 3. Определение конструктора класса PaintEvent</p>



<p>А теперь самое интересное &#8211; реализация метода eventFilter (см. листинг 4). Первым делом удостоверимся, что источник события — это объект сцецны. Получим тип события вызовом метода type() экземпляра класса QEvent и отловим одно из трёх событий:</p>



<ul class="wp-block-list"><li>«Нажатие кнопки мыши» &#8211; QEvent::GraphicsSceneMousePress;</li><li>«Отпускание кнопки мыши» &#8211; QEvent::GraphicsSceneMouseRelease;</li><li>«Перемещение указателя мыши» &#8211; QEvent::GraphicsSceneMouseMove.</li></ul>



<p>Попав в обработчик одного из перечисленных событий можем быть уверены, что пришедшее событие &#8211; событие мыши.</p>



<figure class="wp-block-pullquote"><blockquote><p>В фильтр событий попадают все события генерируемые объектом, для которого этот фильтр установлен: нажатие клавиш, события мыши и другие. Наша цель отфильтровать лишь нужные события.</p></blockquote></figure>



<p>Все события мыши произошедшие с объектом класса QGraphicsScene описываются классом QGraphicsSceneMouseEvent, поэтому необходимо привести указатель на объект события QEvent к классу QGraphicsSceneMouseEvent предоставляющему доступ к информации о позиции курсора и нажатым клавишам мыши.</p>



<h3 class="wp-block-heading" id="2.3 Обработчик события нажатия кнопки">2.3 Обработчик события нажатия кнопки</h3>



<p>Прежде чем приступать к обработке события, необходимо убедиться, что нажата именно левая кнопка мыши, а не другая. Для этого сравним результат вызова метода button() экземпляра класса QGraphicsSceneMouseEvent с константой Qt::LeftButton. Если условие выполняется, то сохраним текущее положение курсора (вызов метода sсenePos() экземпляра события) в параметр startPoint. Установим параметр pressed значение true. Pressed используется в обработчике события перемещения мыши как индикатор того, что нажата левая кнопка и процесс построения отрезка ещё не завершен.</p>



<h3 class="wp-block-heading" id="2.4 Обработчик события отпускания кнопки">2.4 Обработчик события отпускания кнопки</h3>



<p>Обработчик отпускания кнопки содержит такую же проверку на нажатие левой кнопки мыши. После проверки осуществляем рисование отрезка из сохраненной точки в точку текущего положения курсора, используя метод addLine() объекта сцены. Вторым параметром в метод addLine() передается перо pen для стилизации отрезка. Процесс рисования отрезка на этом завершен, поэтому параметру pressed присвоим значение false.</p>



<h3 class="wp-block-heading" id="2.5 Обработчик события перемещения мыши">2.5 Обработчик события перемещения мыши</h3>



<p>Отрисовку вспомогательных отрезков реализуем в третьем обработчике. Проверять нажатие кнопки в этом случае нет смысла, но удалить предыдущий вспомогательный отрезок, если таков был создан ранее, мы просто обязаны. Убедимся, что значение параметра pressed == true, тогда рисуем новый вспомогательный отрезок с использованием другого пера — tempPen.</p>



<p>После реализации необходимых обработчиков возвращаем из функции значение false, потому что нет причин блокировать прохождение событий далее. Реализованные обработчики, в этом конкретном случае, расширяют функционал системных обработчиков, а не заменяют их, поэтому поглощать события нет смысла.</p>



<figure class="wp-block-pullquote"><blockquote><p>Помните! Фильтр событий получает абсолютно все события и поглощение событий сработает для всех событий для которых метод eventFilter вернёт false!</p></blockquote></figure>



<p>Исходные код метода eventFilter приведен в листинге 4.</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: cpp; title: Пример кода с сайта https://zhitenev.ru; notranslate">
bool PaintEvent::eventFilter(QObject* object, QEvent* event) {
    if(object == scene) {
        QGraphicsSceneMouseEvent* mouseEvent = nullptr;
        switch(event-&gt;type()) {
        case QEvent::GraphicsSceneMousePress:
            mouseEvent = static_cast&lt;QGraphicsSceneMouseEvent*&gt;(event);
            if(mouseEvent-&gt;button() == Qt::LeftButton) {
                startPoint = mouseEvent-&gt;scenePos();
                pressed = true;
            }
            break;
        case QEvent::GraphicsSceneMouseRelease:
            mouseEvent = static_cast&lt;QGraphicsSceneMouseEvent*&gt;(event);
            if(mouseEvent-&gt;button() == Qt::LeftButton) {
                scene-&gt;addLine(QLineF(startPoint, mouseEvent-&gt;scenePos()), pen);
                pressed = false;
            }
            break;
        case QEvent::GraphicsSceneMouseMove:
            mouseEvent = static_cast&lt;QGraphicsSceneMouseEvent*&gt;(event);
            if(draftLine != nullptr) {
                scene-&gt;removeItem(draftLine);
                delete draftLine;
                draftLine = nullptr;
            }
            if(pressed)
                draftLine = scene-&gt;addLine(QLineF(startPoint, mouseEvent-&gt;scenePos()), tempPen);
            break;
        default:
            break;
        }
    }
    return false;
}
</pre></div>


<p class="has-text-align-center">Листинг 4. Исходный код метода eventFilter</p>



<p>Запускаем приложение и наслаждаемся готовым результатом (см. рис. 2).</p>



<div class="wp-block-image"><figure class="aligncenter size-full"><img loading="lazy" decoding="async" width="802" height="632" src="https://zhitenev.ru/wp-content/uploads/2021/11/res.jpg" alt="Рисунок 2. Процесс рисования отрезков в готовом приложении" class="wp-image-2844" srcset="https://zhitenev.ru/wp-content/uploads/2021/11/res.jpg 802w, https://zhitenev.ru/wp-content/uploads/2021/11/res-730x575.jpg 730w, https://zhitenev.ru/wp-content/uploads/2021/11/res-285x225.jpg 285w, https://zhitenev.ru/wp-content/uploads/2021/11/res-768x605.jpg 768w" sizes="auto, (max-width: 802px) 100vw, 802px" /><figcaption>Рисунок 2. Процесс рисования отрезков в готовом приложении</figcaption></figure></div>



<h2 class="wp-block-heading" id="3. Выводы">3. Выводы</h2>



<ul class="wp-block-list"><li>Фильтры событий это объекты унаследованные от QObject и реализующие метод bool eventFilter(QObject* object, QEvent* event);</li><li>Фильтры событий позволяют выносить в отдельный класс реализацию обработчиков событий, расширять или переопределять функционал, избежать дублирования кода;</li><li>Один и тот же фильтр может быть применен сразу к нескольким объектам;</li><li>Несколько фильтров может быть применено к одному объекту;</li></ul>



<h2 class="wp-block-heading" id="4. Исходный код">4. Исходный код</h2>



<p>Скачать исходный код можно по ссылке ниже:</p>



<div class="wp-block-file"><a href="https://zhitenev.ru/wp-content/uploads/2021/11/EventListener.zip">EventListener</a><a href="https://zhitenev.ru/wp-content/uploads/2021/11/EventListener.zip" class="wp-block-file__button" download>Скачать</a></div><p>The post <a href="https://zhitenev.ru/otlov-sobytiy-v-qt-s-pomoshiyu-filtrov-sobytiy/">Отлов событий в Qt с помощью фильтров событий</a> first appeared on <a href="https://zhitenev.ru">Блог Ильи Житенёва</a>.</p>]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Краткий справочник по умным указателям С++</title>
		<link>https://zhitenev.ru/kratkiy-spravochnik-po-umnym-ukazatelyam-cpp/</link>
		
		<dc:creator><![CDATA[Илья]]></dc:creator>
		<pubDate>Sun, 17 Oct 2021 12:50:13 +0000</pubDate>
				<category><![CDATA[C++ и Qt]]></category>
		<category><![CDATA[C++]]></category>
		<category><![CDATA[программирование]]></category>
		<category><![CDATA[указатели]]></category>
		<guid isPermaLink="false">https://zhitenev.ru/?p=2827</guid>

					<description><![CDATA[<p>Умные указатели появились в стандарте С++11 и являются фундаментальной основой для написания безопасного кода с точки зрения утечек памяти в современном С++. Благодаря идиоме RAII (Resource Acquisition Is Initialization &#8211; получение ресурса есть инициализация), используемой в умных указателях, они позволяют эффективно работать с указателями на динамически выделенные области памяти. В этой статье вы узнаете об ... <a title="Краткий справочник по умным указателям С++" class="read-more" href="https://zhitenev.ru/kratkiy-spravochnik-po-umnym-ukazatelyam-cpp/" aria-label="Больше на Краткий справочник по умным указателям С++">Читать далее</a></p>
<p>The post <a href="https://zhitenev.ru/kratkiy-spravochnik-po-umnym-ukazatelyam-cpp/">Краткий справочник по умным указателям С++</a> first appeared on <a href="https://zhitenev.ru">Блог Ильи Житенёва</a>.</p>]]></description>
										<content:encoded><![CDATA[<p>Умные указатели появились в стандарте С++11 и являются фундаментальной основой для написания безопасного кода с точки зрения утечек памяти в современном С++. Благодаря идиоме RAII (Resource Acquisition Is Initialization &#8211; получение ресурса есть инициализация), используемой в умных указателях, они позволяют эффективно работать с указателями на динамически выделенные области памяти. В этой статье вы узнаете об основных моментах при работе с этими полезными типами.</p>



<span id="more-2827"></span>



<pre class="wp-block-preformatted"><strong>Содержание статьи</strong>

<a href="#1. Общая информация" title="#1. Общая информация">1. Общая информация</a>
<a href="#2. std::unique_ptr" title="#2. std::unique_ptr">2. std::unique_ptr</a>
<a href="#3. std::shared_ptr" title="#3. std::shared_ptr">3. std::shared_ptr</a>
<a href="#4. std::weak_ptr" title="#4. std::weak_ptr">4. std::weak_ptr</a></pre>



<p>Начнем с общих черт и описания в целом такого понятия, как умные указатели, а затем рассмотрим каждый вид умных указателей по отдельности. Рассмотрим их отличия, области применения, слабые и сильные места. Поехали!</p>



<h2 class="wp-block-heading" id="1. Общая информация">1. Общая информация</h2>



<ul class="wp-block-list"><li>Определение умных указателей содержится в заголовочном файле &lt;memory&gt;</li><li>Умные указатели назвали &#8220;умными&#8221; потому, что они хранят указатель на объект или некий ресурс, а также осуществляют владение этими данными. Они основаны на идиоме RAII.</li><li>Указатели <em>unique_ptr </em>и <em>shared_ptr </em>имеют перегруженные оператора доступа <em>*</em> и <em>-&gt;</em>, поэтому умные указатели могут быть разыменованы как и обычные &#8220;сырые&#8221; (raw) указатели.</li><li>Для получения сырого указатели из объекта <em>unique_ptr </em>и <em>shared_ptr </em>используйте метод <em>get()</em>.</li><li>Метод <em>.get()</em> может быть полезен, когда вам необходимо передать в функцию сырой указатель для отслеживания состояния объекта (желательно не изменять объект, которым владеет умный указатель, через сырые указатели).</li></ul>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: cpp; title: Пример кода с сайта https://zhitenev.ru; notranslate">
void useObject(MyType* pObj) { }
useObject(mySmartPtr.get());
</pre></div>


<ul class="wp-block-list"><li><em>unique_ptr </em>(начиная со стандарта C++11) и <em>shared_ptr </em>(со стандарта C++17) получили специализацию шаблона для управления массивами данных. Нововведение заключается во внедрении вызова оператора <em>delete[]</em> при удалении управляемого объекта). Это может быть полезно, когда вы получаете указатель на массив, например, из какой-либо библиотеки. Однако, если возможно, старайтесь использовать вместо массивов стандартные контейнеры такие как <em>std::vector&lt;T></em> или <em>std::array&lt;T></em>.</li><li><strong>Напоминаю!</strong> Не используйте в своем коде <em>auto_ptr</em>! Этот указатель имеет ряд критических проблем, отмечен устаревшим в стандарте C++11 и полностью удален в стандарте С++17. Взамен используйте <em>unique_ptr</em>. Для поиска таких мест можете воспользоваться настройкой <em>modernize-replace-auto-ptr</em> утилиты Clang Tidy для автоматизации рефакторинга.</li><li>В стандартах C++17/20 отсутствует выведение аргументов шаблонов классов (class template argument deduction &#8211; CTAD) для умных указателей. Поэтому компилятор не в состоянии отличить указатель на массив от указателя на объекты не массивы созданные оператором <em>new()</em>.</li><li>Начиная со стандарта С++20 появились возможность применение атомарных указателей <em>std::atomic&lt;std::shared_ptr&lt;T>></em> и <em>std::atomic&lt;std::weak_ptr&lt;T>></em>.  Также, в этом стандарты были отмечены устаревшими глобальные атомарные функции для умных указателей, доступных с С++11.</li><li>C++20 добавляет различные функции конструирования <em>*_for_overwrite</em>, которые не принимают аргументов конструктора и используют инициализацию по умолчанию (аналогичную применению оператора new T). Это позволяет избежать ненужной инициализации в ситуациях, когда начальное значение никогда не считывается (например, чтение в буфер).</li></ul>



<h2 class="wp-block-heading" id="2. std::unique_ptr">2. std::unique_ptr</h2>



<p>std::unique_ptr это легковесный указатель монопольно владеющий ресурсом.</p>



<ul class="wp-block-list"><li><em>std::unique_ptr</em> уничтожает управляемый объект когда указатель покидает область видимости. Для удаления объекта и назначения (при необходимости) нового объекта в управление <em>std::unique_ptr</em> используйте метод <em>reset()</em>.</li><li><em>std::unique_ptr</em> не может быть скопирован, но его можно переместить.</li><li>Обычно, указатель имеет небольшой размер, сопоставимый с размером обычного сырого указателя на объект или размер двух указателей, если при создании умного указателя устанавливается пользовательская функция очистки памяти.</li></ul>



<h3 class="wp-block-heading">Создание</h3>



<p>Наилучший и самый безопасный способ создания <em>unique_ptr </em>это использование переменной типа <em>auto </em>и присваивание ей результата выполнения статического метода <em>std::make_unique</em></p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: cpp; title: Пример кода с сайта https://zhitenev.ru; notranslate">
auto pObj = make_unique&lt;MyType&gt;(...);
</pre></div>


<p>Уникальный указатель можно создать и с явным указанием оператора <em>new</em>:</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: cpp; title: Пример кода с сайта https://zhitenev.ru; notranslate">
unique_ptr&lt;MyType&gt; pObject(new MyType(...));
</pre></div>


<p>Код выше имеет два недостатка в сравнении с первым способом: </p>



<ol class="wp-block-list"><li>тип указывается дважды</li><li>используется сырой указатель, получаемый оператором <em>new </em>для инициализации умного указателя</li></ol>



<p>Но в некоторых случаях второй пример может быть полезен и даже незаменимым:</p>



<ol class="wp-block-list"><li>для создания умного указателя с пользовательским оператором удаления (об этом далее)</li><li>для создания полиморфных объектов, см. пример ниже</li></ol>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: cpp; title: Пример кода с сайта https://zhitenev.ru; notranslate">
unique_ptr&lt;Base&gt; pObject(new MyDerived(...));
</pre></div>


<h3 class="wp-block-heading">Пользовательские функции (операторы) удаления</h3>



<p>Оператор или функция удаления это функциональный объект используемый для удаления ресурса. По умолчанию используются стандартные операторы <em>delete</em> или <em>delete[]</em>. </p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: cpp; title: Пример кода с сайта https://zhitenev.ru; notranslate">
struct DelFn {  
  void operator()(MyTy* p) {
    p-&gt;SpecialDelete(); 
    delete p;
  }
};

using my_ptr = unique_ptr&lt;MyTy, DelFn&gt;;
</pre></div>


<pre class="wp-block-preformatted">На мой взгляд ещё удобнее использовать в качестве пользовательского оператора удаления лямбда-функции.</pre>



<ul class="wp-block-list"><li>Оператор удаления не вызывается, если указатель содержит нулевой указатель</li><li>Метод <em>get_deleter()</em> возвращает не константную ссылку на оператор удаления. Этот факт позволяет использовать ссылку и для замены оператора удаления.</li></ul>



<h3 class="wp-block-heading">Передача в функцию</h3>



<p>Как было сказано выше, <em>unique_ptr </em>может быть только перемещаемым объектом. Перемещение следует выполнять функцией <em>std::move()</em>, чтобы явно отобразить передачу владения объектом от одного указателя к другому:</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: cpp; title: Пример кода с сайта https://zhitenev.ru; notranslate">
auto pObj = make_unique&lt;MyType&gt;(...);
func(std::move(pObj));
// pObj является невалидным указателем после вызова std::move()
</pre></div>


<h3 class="wp-block-heading">Дополнительная информация</h3>



<ul class="wp-block-list"><li>Метод <em>reset()</em> &#8211; сбрасывает указатель (владеемый объект удаляется)</li><li><em>unique_ptr </em>&#8211; полезен при реализации идиомы &#8220;pimpl&#8221; (pointer to implementation &#8211; указатель на реализацию). </li><li>Уникальный указатель рассматривается в первую очередь как кандидат на возвращаемый объект из фабричных методов. Если уникальный указатель не подходит, тогда рассмотрите возможность применения <em>shared_ptr</em> (или <em>weak_ptr</em>).</li></ul>



<h2 class="wp-block-heading" id="3. std::shared_ptr">3. std::shared_ptr</h2>



<p>Общий (shared) указатель<em> </em>обеспечивает &#8220;распределенное&#8221; владение объектом. Множество <em>shared_ptr </em>могут указывать на один и тот же объект. Рядом с каждым объектом создается счетчик указателей, ссылающегося на него. Уничтожение объекта происходит в тот момент, когда уничтожается последний указатель на него (<em>shared_ptr </em>или <em>weak_ptr</em>).</p>



<ul class="wp-block-list"><li><em>shared_ptr </em>могут быть перемещены или скопированы</li><li><em>shared_ptr </em>имеет размер двух указателей: первый на контролируемый объект и второй на управляющий блок</li><li>Управляющий блок содержит счетчик shared_ptr, счетчик weak_ptr, указатели на оператор удаления и аллокатор.</li></ul>



<h3 class="wp-block-heading">Создание</h3>



<p>Рекомендуемый метод, как и с уникальными указателями, это использованием статического метода <em>make_shared</em>:</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: cpp; title: Пример кода с сайта https://zhitenev.ru; notranslate">
auto pObj = make_shared&lt;MyType&gt;(...)
</pre></div>


<p><em>make_shared </em>выделяет место под контролируемый объект и рядом создает управляющий блок. Это позволяет компактнее использовать динамическую память.</p>



<h3 class="wp-block-heading">Пользовательские операторы удаления</h3>



<p>Указатель на оператор удаления хранится в управляющем блоке. При создании <em>shared_ptr </em>с помощью конструктора можно указать пользовательский оператор удаления. </p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: cpp; title: Пример кода с сайта https://zhitenev.ru; notranslate">
void DelFn(MyTp* p) {
    if (p) p-&gt;OnDelete(); 
    delete p;
}

shared_ptr&lt;MyTp&gt; ptr(new MyTp(), DelFn);
</pre></div>


<ul class="wp-block-list"><li>Оператор удаления обязан корректно обрабатывать нулевые указатели. Допускаются ситуации, когда оператор удаления может быть вызван в пустых указателях <em>shared_ptr</em>.</li><li>Метод <em>get_deleter()</em> возвращает неконстантный указатель на оператор удаления.</li></ul>



<h3 class="wp-block-heading">Передача в функцию</h3>



<p>Владение объектом в указателе <em>shared_ptr </em>не является монопольным, поэтому вы можете поделиться указателем на объект владения (для указателей <em>unique_ptr </em>не рекомендуется создавать несколько <em>unique_ptr </em>указателей на один и тот же объект). Счетчик shared указателей обновляется автоматически при создании и уничтожении объектов <em>shared_ptr</em>, но за этот функционал конечно приходится немного заплатить. Shared указатели можно перемещать методом <em>std::move()</em>.</p>



<p>Для получения сырого указателя используйте метод <em>get()</em>.</p>



<h3 class="wp-block-heading">Дополнительная информация</h3>



<ul class="wp-block-list"><li>Изменение состояния счетчика указателей является атомарным, но доступ к указателю не является потоко-безопасным. </li><li>Для создания <em>shared_ptr </em>указателей на <em>*this</em> используйте метод <em>shared_from_this()</em> и унаследуйте класс от <em>std::enable_shared_from_this</em>.</li><li>Приведение типов указателей может выполняться функциями <em>dynamic_pointer_cast</em>, <em>static_pointer_cast </em>или <em>reinterpret_pointer_cast</em>.</li><li>В виду не монопольности владения объектом, неаккуратное использование shared указателей может приводить к возникновению циклических зависимостей, когда несколько указателей ссылаются друг на друга и не могут уничтожить объект, что может приводить у утечке памяти</li></ul>



<h2 class="wp-block-heading" id="4. std::weak_ptr">4. std::weak_ptr</h2>



<p>Слабые (weak) указатели содержат слабую ссылку на объект, который управляется shared указателем. <em>std::weak_ptr</em> для доступа к указываемому объекту обязан быть преобразован в <em>std::shared_ptr</em> методом <em>lock()</em>.</p>



<ul class="wp-block-list"><li>Одним из примеров, где слабые указатели находят широкое применение это кэширование. Например, ресурсы выделяемые операционной системой всегда представляют собой слабые ссылки и перед использованием нужно убедится, что это ресурс всё ещё является доступным.</li><li>Применение слабых указателей позволяет избежать циклической зависимости при применении shared указателей.</li></ul>



<h3 class="wp-block-heading">Создание</h3>



<p>Слабые указатели создаются из <em>shared_ptr</em>, но перед их использованием их нужно конвертировать обратно в <em>shared_ptr</em>.</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: cpp; title: Пример кода с сайта https://zhitenev.ru; notranslate">
std::weak_ptr pWeak = pSharedPtr;

if (auto observe = pWeak.lock()) {
    // Объект существует
} else {
   // shared_ptr является пустым
}
</pre></div>


<p>При создании <em>std::weak_ptr</em> из <em>std::shared_ptr</em> происходит инкремент счетчика слабых указателей в управляющем блоке shared указателя. Это происходит даже в том случае, если все shared указатели, ссылающиеся на объект, были уничтожены, тогда управляющий блок существует до тех пора, пока существует хотя бы один <em>std::weak_ptr</em>, указываемый на него. Это может быть проблемой если управляющий блок был выделен в памяти рядом с управляемым объектом (например при использовании метода <em>make_shared&lt;T>()</em>). Получается, что управляемый объект уничтожается, а вот управляющий блок остается висеть в памяти.</p>



<h3 class="wp-block-heading">Дополнительная информация</h3>



<ul class="wp-block-list"><li>метод <em>use_count()</em> возвращает количество shared указателей, которые управляют одним и тем же объектом</li><li>используйте <em>expired()</em> для проверки факта существования управляемого объекта</li><li>слабые указатели не содержат операторов <em>*</em> и <em>-></em>, поэтому единственный способ получить доступ к управляемому объекту &#8211; конвертировать <em>weak_ptr </em>в <em>shared_ptr</em> методом <em>lock()</em>.</li></ul>



<h2 class="wp-block-heading">В качестве выводов</h2>



<p>Эта статья познакомила или напомнила вам важную информацию об умных указателях в современном C++. Представленная здесь информация должна облегчить работу с основными типами умных указателей и идиомой RAII.</p>



<hr class="wp-block-separator"/>



<p>Пост является переводом статьи: <a href="https://www.cppstories.com/2021/smart-ptr-ref-card/">https://www.cppstories.com/2021/smart-ptr-ref-card/</a></p><p>The post <a href="https://zhitenev.ru/kratkiy-spravochnik-po-umnym-ukazatelyam-cpp/">Краткий справочник по умным указателям С++</a> first appeared on <a href="https://zhitenev.ru">Блог Ильи Житенёва</a>.</p>]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Синхронизация потоков и безопасная работа с общими данными в многопоточной среде на C++ и Qt</title>
		<link>https://zhitenev.ru/thread-synchronize-and-safe-operation-with-global-data-at-multi-thread-environment-on-cpp-and-qt/</link>
		
		<dc:creator><![CDATA[Илья]]></dc:creator>
		<pubDate>Sat, 25 Sep 2021 16:06:33 +0000</pubDate>
				<category><![CDATA[C++ и Qt]]></category>
		<category><![CDATA[C++]]></category>
		<category><![CDATA[Qt]]></category>
		<category><![CDATA[Многопоточность]]></category>
		<guid isPermaLink="false">https://zhitenev.ru/?p=2822</guid>

					<description><![CDATA[<p>Представьте, что вы с другом пишете реферат и у вас один учебник на двоих. Вы сели писать реферат, написали несколько страниц, оставили учебник открытым с намерением дописать его позже и ушли по своим делам. В это время ваш друг, который живет с вами в одной комнате, вернулся домой, увидел, что у вас что-то написано на ... <a title="Синхронизация потоков и безопасная работа с общими данными в многопоточной среде на C++ и Qt" class="read-more" href="https://zhitenev.ru/thread-synchronize-and-safe-operation-with-global-data-at-multi-thread-environment-on-cpp-and-qt/" aria-label="Больше на Синхронизация потоков и безопасная работа с общими данными в многопоточной среде на C++ и Qt">Читать далее</a></p>
<p>The post <a href="https://zhitenev.ru/thread-synchronize-and-safe-operation-with-global-data-at-multi-thread-environment-on-cpp-and-qt/">Синхронизация потоков и безопасная работа с общими данными в многопоточной среде на C++ и Qt</a> first appeared on <a href="https://zhitenev.ru">Блог Ильи Житенёва</a>.</p>]]></description>
										<content:encoded><![CDATA[<p>Представьте, что вы с другом пишете реферат и у вас один учебник на двоих. Вы сели писать реферат, написали несколько страниц, оставили учебник открытым с намерением дописать его позже и ушли по своим делам. В это время ваш друг, который живет с вами в одной комнате, вернулся домой, увидел, что у вас что-то написано на листах, а рядом лежит учебник и решил, что вы закончили свой реферат. Он начинает листать учебник, выкидывая все ваши закладки, находит нужный ему материал и пишет свой реферат. Спустя несколько часов вы возвращаетесь домой, ваш друг уже спит, и вы решаете дописать реферат. Садитесь за письменный стол и «о, ужас!» все ваши закладки сбиты и вы не можете найти то место, где остановились. Неприятная ситуация? А ведь такая же может возникнуть в многопоточной среде, когда одни и те же внешние данные используются несколькими потоками. О том, как с этим жить, я расскажу в этой статье.</p>



<span id="more-2822"></span>



<pre class="wp-block-preformatted"><strong>Содержание статьи</strong>

<a href="#Проблема" title="#Проблема">1. Проблема</a>
2. <a href="#Мьютексы (mutex)" title="#Мьютексы (mutex)">Мьютексы (mutex)</a>
3. <a href="#Семафоры (semaphore)" title="#Семафоры (semaphore)">Семафоры (semaphore)</a>
4. <a href="#Переменные условия­ (Condition variables или Wait conditions)" title="#Переменные условия­ (Condition variables или Wait conditions)">Переменные условия ­(Condition variables или Wait conditions)</a>
5. <a href="#Другие способы" title="#Другие способы">Другие способы</a>
6. <a href="#Выводы" title="#Выводы">Выводы</a></pre>



<p>Описанная выше задача известная как race conditions &#8211; или просто &#8220;гонки&#8221;, когда несколько параллельных задач конкурируют за общие ресурсы. Решается она разграничением доступа к данным во времени. Для этих целей используются следующие сущности:</p>



<figure class="wp-block-table"><table><tbody><tr><td>Qt</td><td class="has-text-align-left" data-align="left">C++</td></tr><tr><td>QMutex<br>QSemaphore<br>QWaitCondition</td><td class="has-text-align-left" data-align="left">std::mutex<br>std::semaphore<br>std::condition_variable</td></tr></tbody></table></figure>



<p>Применение этих сущностей я покажу на примерах и расскажу как они устроены и как их применять. Приведённый далее код будет написан на Qt, т.&nbsp;к.  С++ код практически идентичен, есть лишь некоторые отличия между чистым С++ и Qt, и в таких случаях я приведу оба примера кода.</p>



<h2 class="wp-block-heading" id="Проблема">1. Проблема</h2>



<p>Давайте перейдем к примерам и поставим задачу. Пусть имеется 2 банковских счета с 1000 рублей на каждом. Два пользователя одновременно инициируют операции перевода между ними: со счета №1 переводится 500 рублей на счет №2, а со счета №2 на счет №1 переводится 1200 рублей. Чтобы переводы выполнялись «одновременно», создадим объект выполняющий операцию перевода и его выполнение запустим в двух разных потоках.</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: cpp; title: Пример кода с сайта https://zhitenev.ru; notranslate">
#include &lt;QCoreApplication&gt;
#include &lt;QThread&gt;
#include &lt;QDebug&gt;

struct Account {
    int balance{1000};
};

class Transfer : public QThread {
    Account&amp; from;
    Account&amp; to;
    int amount;
    void run() override {
        if(from.balance &gt;= amount) {
            from.balance -= amount;
            QThread::usleep(1);
            to.balance += amount;
        }
    }
public:
     Transfer(Account&amp; from, 
     Account&amp; to,
     int amount, 
     QObject* parent = nullptr) : 
     QThread(parent),
     from(from),
     to(to),
     amount(amount)
    {

    }
};

int main(int argc, char *argv&#x5B;])
{
    QCoreApplication a(argc, argv);

    Account acc1;
    Account acc2;

    Transfer t1(acc1, acc2, 500);
    Transfer t2(acc2, acc1, 1200);

    t1.start();
    t2.start();

    t1.wait();
    t2.wait();

    qDebug() &lt;&lt; &quot;Acc1.balance=&quot; &lt;&lt; acc1.balance;
    qDebug() &lt;&lt; &quot;Acc2.balance=&quot; &lt;&lt; acc2.balance;


    return a.exec();
}
</pre></div>


<p>Запускаем программу и получаем следующий результат:</p>



<pre class="wp-block-preformatted">Acc1.balance = 500
Acc2.balance = 1500</pre>



<p>Ну что, вас ничего не смущает? Как мы видим, выполнилась лишь первая операция — перевод 500 рублей с первого счета на второй, а перевести 1200 рублей со второго на первый не удалось, т.&nbsp;к. в момент начала операции на втором счете ещё не было нужной суммы. Давайте решать эту проблему.</p>



<h2 class="wp-block-heading" id="Мьютексы (mutex)">2. Мьютексы (mutex)</h2>



<p>В переводе с английского mutex (мьютекс) означает «взаимное исключение». Мьютекс гарантирует, что из всех потоков, которые попытались его захватить, будет запущен лишь один поток в одну единицу времени, а остальные будут отправлены в очередь ожидания.</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: cpp; title: Пример кода с сайта https://zhitenev.ru; notranslate">
QMutex m;
...
m.lock();
// Осуществляем монопольный доступ
m.unlock();
</pre></div>


<p>Будьте осторожны с мьютексом. Его надо обязательно отпустить, иначе его нельзя будет захватить. Поскольку это частая ошибка и в больших проектах выявить её очень сложно, то были придуманы сущности для работы с мьютексами с использованием идиомы RAII (Resource Acquisition Is Initialization).</p>



<figure class="wp-block-table"><table><tbody><tr><td class="has-text-align-left" data-align="left">Qt</td><td class="has-text-align-left" data-align="left">C++</td></tr><tr><td class="has-text-align-left" data-align="left">QMutexLocker</td><td class="has-text-align-left" data-align="left">std::lock_guard</td></tr></tbody></table></figure>



<p>RAII — идиома, смысл которой заключается в том, что получение ресурса совмещено с инициализацией объекта, а освобождение с уничтожением объекта. Таким образом, вместе с уничтожением объекта QMutexLocker автоматически вызывается unlock мьютекса, если требуется. Давайте применим эти сущности для исправления нашего кода с помощью мьютексов.</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: cpp; title: Пример кода с сайта https://zhitenev.ru; notranslate">
#include &lt;QCoreApplication&gt;
#include &lt;QThread&gt;
#include &lt;QDebug&gt;
#include &lt;QMutex&gt;
#include &lt;QMutexLocker&gt;

QMutex m;

struct Account {
    int balance{1000};
};

class Transfer : public QThread {
    Account&amp; from;
    Account&amp; to;
    int amount;
    void run() override {
        QMutexLocker lk(&amp;m);
        if(from.balance &gt;= amount) {
            from.balance -= amount;
            QThread::usleep(1);
            to.balance += amount;
        }
    }
public:
    Transfer(Account&amp; from, Account&amp; to, int amount, QObject* parent = nullptr) :
     QThread(parent),
     from(from),
     to(to),
     amount(amount)
    {

    }
};

int main(int argc, char *argv&#x5B;])
{
    QCoreApplication a(argc, argv);

    Account acc1;
    Account acc2;

    Transfer t1(acc1, acc2, 500);
    Transfer t2(acc2, acc1, 1200);

    t1.start();
    t2.start();

    t1.wait();
    t2.wait();

    qDebug() &lt;&lt; &quot;Acc1.balance=&quot; &lt;&lt; acc1.balance;
    qDebug() &lt;&lt; &quot;Acc2.balance=&quot; &lt;&lt; acc2.balance;


    return a.exec();
}
</pre></div>


<p>Получаем результат:</p>



<pre class="wp-block-preformatted">Acc1.balance = 1700
Acc2.balance = 300</pre>



<p>Другое дело!</p>



<h2 class="wp-block-heading" id="Семафоры (semaphore)">3. Семафоры (semaphore)</h2>



<p>Семафоры, по сути своей, есть те же мьютексты, но с одним отличием. Семафор является счетным объектом, т.&nbsp;к. его можно выставить несколько раз и потом столько же раз его можно отпустить. Т.е. мьютекс является как бы бинарным — выставлен или нет, а семафор является счетчиком.</p>



<p>Для дальнейших примером давайте рассматривать следующую задачу. Первый поток — это работодатель, который каждую секунду в течение 12 секунд перечисляет в кошелек отцу 400 рублей. У отца есть сын Вася, который просит у отца на мороженное стоимостью 500 рублей. Если у отца достаточно денег, он выдает ему, если денег недостаточно, то и мороженного нет. Баланс кошелька мы можем хранить в семафоре. Тогда получим следующее решение:</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: cpp; title: Пример кода с сайта https://zhitenev.ru; notranslate">
#include &lt;QCoreApplication&gt;
#include &lt;QThread&gt;
#include &lt;QDebug&gt;
#include &lt;QSemaphore&gt;

struct Account {
    QSemaphore balance;
};

class BuyIceCream : public QThread {
    Account&amp; from;
    static const int cost = 500;
    QString name;
    void run() override {
        while (1) {
            from.balance.acquire(cost);
            qDebug() &lt;&lt; name &lt;&lt; &quot;! Enough money! Wallet = &quot; &lt;&lt; from.balance.available()+cost &lt;&lt; &quot;-&quot; &lt;&lt; cost &lt;&lt; &quot;=&quot; &lt;&lt; from.balance.available();
        }


    }
public:
    BuyIceCream(QStringView name, Account&amp; from, QObject* parent = nullptr) :
    QThread(parent),
     from(from),
     name(name.toString())
{}
};

class Transfer : public QThread {
    Account&amp; to;
    int amount;
    void run() override {
        for(int i = 0; i &lt; 12; ++i) {
            QThread::msleep(1000);

            qDebug() &lt;&lt; i+1 &lt;&lt; &quot;) Transfer from WORK: &quot; &lt;&lt; amount &lt;&lt; &quot;. Wallet = &quot; &lt;&lt; to.balance.available()+amount;
            to.balance.release(amount);

        }
    }
public:
    Transfer(Account&amp; to, int amount, QObject* parent = nullptr) :
    QThread(parent),
    to(to),
    amount(amount)
    {

    }
};

int main(int argc, char *argv&#x5B;])
{
    QCoreApplication a(argc, argv);

    Account fathersWallet;

    Transfer t1(fathersWallet, 400);
    BuyIceCream son1(u&quot;Vasya&quot;, fathersWallet);

    t1.start();
    son1.start();

    t1.wait();

    son1.quit();


    return a.exec();
}
</pre></div>


<p>и выводимый результат:</p>



<pre class="wp-block-preformatted">1 ) Transfer from WORK:  400 . Wallet =  400
2 ) Transfer from WORK:  400 . Wallet =  800
"Vasya" ! Enough money! Wallet =  800 - 500 = 300
3 ) Transfer from WORK:  400 . Wallet =  700
"Vasya" ! Enough money! Wallet =  700 - 500 = 200
4 ) Transfer from WORK:  400 . Wallet =  600
"Vasya" ! Enough money! Wallet =  600 - 500 = 100
5 ) Transfer from WORK:  400 . Wallet =  500
"Vasya" ! Enough money! Wallet =  500 - 500 = 0
6 ) Transfer from WORK:  400 . Wallet =  400
7 ) Transfer from WORK:  400 . Wallet =  800
"Vasya" ! Enough money! Wallet =  800 - 500 = 300
8 ) Transfer from WORK:  400 . Wallet =  700
"Vasya" ! Enough money! Wallet =  700 - 500 = 200
9 ) Transfer from WORK:  400 . Wallet =  600
"Vasya" ! Enough money! Wallet =  600 - 500 = 100
10 ) Transfer from WORK:  400 . Wallet =  500
"Vasya" ! Enough money! Wallet =  500 - 500 = 0
11 ) Transfer from WORK:  400 . Wallet =  400
12 ) Transfer from WORK:  400 . Wallet =  800
"Vasya" ! Enough money! Wallet =  800 - 500 = 300
</pre>



<h2 class="wp-block-heading" id="Переменные условия­ (Condition variables или Wait conditions)">4. Переменные условия­ (Condition variables или Wait conditions)</h2>



<p>Переменные ожидания (возможно, вы предложите более читабельный русский перевод) (далее по тексту CV) имеют другую отличительную особенность: с их помощью можно пробудить либо один поток, либо все, что ожидают захвата CV.</p>



<figure class="wp-block-table"><table><tbody><tr><td>Qt</td><td>C++</td></tr><tr><td>QWaitCondition</td><td>std::condition_vairable</td></tr></tbody></table></figure>



<p>Давайте также разберем на примере задачи про отца и мороженное, только теперь у отца будет два сына, содержимое кошелька будет снова определяться переменной типа int, а не семафором.</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: cpp; title: Пример кода с сайта https://zhitenev.ru; notranslate">
#include &lt;QCoreApplication&gt;
#include &lt;QThread&gt;
#include &lt;QDebug&gt;
#include &lt;QWaitCondition&gt;
#include &lt;QMutex&gt;

QMutex m;

struct Account {
    int balance{0};
    QWaitCondition enoughMoney;
};

class BuyIceCream : public QThread {
    Account&amp; from;
    static const int cost = 500;
    QString name;
    void run() override {
        while (1) {
            QMutexLocker lk(&amp;m);
            from.enoughMoney.wait(&amp;m);

            if(from.balance &gt;= cost) {
                qDebug() &lt;&lt; name &lt;&lt; &quot;! Enough money! Wallet = &quot; &lt;&lt; from.balance &lt;&lt; &quot;-&quot; &lt;&lt; cost &lt;&lt; &quot;=&quot; &lt;&lt; from.balance-cost;
                from.balance -= cost;
            }
        }


    }
public:
    BuyIceCream(QStringView name, Account&amp; from, QObject* parent = nullptr) :
     QThread(parent),
    from(from),
     name(name.toString())
 {}
};

class Transfer : public QThread {
    Account&amp; from;
    Account&amp; to;
    int amount;
    void run() override {
        for(int i = 0; i &lt; 12; ++i) {
            QThread::msleep(1000);

            QMutexLocker lk(&amp;m);
            if(from.balance &gt;= amount) {
                from.balance -= amount;
                to.balance += amount;
                qDebug() &lt;&lt; i+1 &lt;&lt; &quot;) Transfer from WORK: &quot; &lt;&lt; amount &lt;&lt; &quot;. Wallet = &quot; &lt;&lt; to.balance;
            }

            to.enoughMoney.notify_one();
        }
    }
public:
    Transfer(Account&amp; from, Account&amp; to, int amount, QObject* parent = nullptr) :
     QThread(parent),
     from(from), to(to),
     amount(amount)
    {

    }
};

int main(int argc, char *argv&#x5B;])
{
    QCoreApplication a(argc, argv);

    Account work;
    work.balance = 10&#039;0000&#039;000;
    Account fathersWallet;

    Transfer t1(work, fathersWallet, 900);
    BuyIceCream son1(u&quot;Vasya&quot;, fathersWallet);
    BuyIceCream son2(u&quot;Petya&quot;, fathersWallet);

    t1.start();
    son1.start();
    son2.start();

    t1.wait();

    son1.quit();
    son2.quit();


    return a.exec();
}
</pre></div>


<p>Обратите внимание, что в строке 54 мы вызываем метод notify_one(), который пробуждает один поток, поэтому лишь один сын сможет купить мороженное и отец может накапливать деньги.</p>



<p>Получаемый результат:</p>



<pre class="wp-block-preformatted">1 ) Transfer from WORK:  1400 . Wallet =  1400
"Vasya" ! Enough money! Wallet =  1400 - 500 = 900
2 ) Transfer from WORK:  1400 . Wallet =  2300
"Petya" ! Enough money! Wallet =  2300 - 500 = 1800
3 ) Transfer from WORK:  1400 . Wallet =  3200
"Vasya" ! Enough money! Wallet =  3200 - 500 = 2700
4 ) Transfer from WORK:  1400 . Wallet =  4100
"Petya" ! Enough money! Wallet =  4100 - 500 = 3600
5 ) Transfer from WORK:  1400 . Wallet =  5000
"Vasya" ! Enough money! Wallet =  5000 - 500 = 4500
6 ) Transfer from WORK:  1400 . Wallet =  5900
"Petya" ! Enough money! Wallet =  5900 - 500 = 5400
7 ) Transfer from WORK:  1400 . Wallet =  6800
"Vasya" ! Enough money! Wallet =  6800 - 500 = 6300
8 ) Transfer from WORK:  1400 . Wallet =  7700
"Petya" ! Enough money! Wallet =  7700 - 500 = 7200
9 ) Transfer from WORK:  1400 . Wallet =  8600
"Vasya" ! Enough money! Wallet =  8600 - 500 = 8100
10 ) Transfer from WORK:  1400 . Wallet =  9500
"Petya" ! Enough money! Wallet =  9500 - 500 = 9000
11 ) Transfer from WORK:  1400 . Wallet =  10400
"Vasya" ! Enough money! Wallet =  10400 - 500 = 9900
12 ) Transfer from WORK:  1400 . Wallet =  11300
"Petya" ! Enough money! Wallet =  11300 - 500 = 10800</pre>



<figure class="wp-block-pullquote"><blockquote><p><em><strong>Обращение к CV не является потокобезопасным, поэтому необходимо использовать мьютекс</strong></em></p></blockquote></figure>



<p>Если же мы захотим порадовать мороженным обоих сыновей, при наличие такой возможности, то используем метод notify_all(). Для наглядного решения уменьшим зарплату отца до 900 рублей. Получим следующий результат:</p>



<pre class="wp-block-preformatted">1 ) Transfer from WORK:  900 . Wallet =  900
"Vasya" ! Enough money! Wallet =  900 - 500 = 400
2 ) Transfer from WORK:  900 . Wallet =  1300
"Vasya" ! Enough money! Wallet =  1300 - 500 = 800
"Petya" ! Enough money! Wallet =  800 - 500 = 300
3 ) Transfer from WORK:  900 . Wallet =  1200
"Vasya" ! Enough money! Wallet =  1200 - 500 = 700
"Petya" ! Enough money! Wallet =  700 - 500 = 200
4 ) Transfer from WORK:  900 . Wallet =  1100
"Vasya" ! Enough money! Wallet =  1100 - 500 = 600
"Petya" ! Enough money! Wallet =  600 - 500 = 100
5 ) Transfer from WORK:  900 . Wallet =  1000
"Vasya" ! Enough money! Wallet =  1000 - 500 = 500
"Petya" ! Enough money! Wallet =  500 - 500 = 0
6 ) Transfer from WORK:  900 . Wallet =  900
"Vasya" ! Enough money! Wallet =  900 - 500 = 400
7 ) Transfer from WORK:  900 . Wallet =  1300
"Vasya" ! Enough money! Wallet =  1300 - 500 = 800
"Petya" ! Enough money! Wallet =  800 - 500 = 300
8 ) Transfer from WORK:  900 . Wallet =  1200
"Vasya" ! Enough money! Wallet =  1200 - 500 = 700
"Petya" ! Enough money! Wallet =  700 - 500 = 200
9 ) Transfer from WORK:  900 . Wallet =  1100
"Vasya" ! Enough money! Wallet =  1100 - 500 = 600
"Petya" ! Enough money! Wallet =  600 - 500 = 100
10 ) Transfer from WORK:  900 . Wallet =  1000
"Vasya" ! Enough money! Wallet =  1000 - 500 = 500
"Petya" ! Enough money! Wallet =  500 - 500 = 0
11 ) Transfer from WORK:  900 . Wallet =  900
"Vasya" ! Enough money! Wallet =  900 - 500 = 400
12 ) Transfer from WORK:  900 . Wallet =  1300
"Petya" ! Enough money! Wallet =  1300 - 500 = 800
"Vasya" ! Enough money! Wallet =  800 - 500 = 300</pre>



<p>Теперь в январе, июне и ноябре мороженное ест лишь один сын (к счастью для Васи и к несчастью для Пети).</p>



<p>В С++ CV предоставляют лаконичный интерфейс, позволяя совместить проверку условия и ожидание CV:</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: cpp; title: Пример кода с сайта https://zhitenev.ru; notranslate">
std::condition_variable cw
std::mutex m;
…
int balance;
std::unique_lock&lt;std::mutex&gt; lk(m);
cw.wait(lk, &#x5B;]{
    return (balance &gt; cost);
});
</pre></div>


<p>В метод wait, помимо мьютекста, передается предикат, который задает некоторое условие для пробуждения потока при получении сигнала от CV. Т.е. при получении уведомления от работодателя о перечислении денег, предикат проверит, хватает ли денег на мороженное, и если да, то только тогда выполнение потока будет продолжено, а иначе он снова уснет.</p>



<h2 class="wp-block-heading" id="Другие способы">5. Другие способы</h2>



<p>Вы не обязаны применять именно эти сущности, поэтому некоторые задачи можно решить и иначе. В задаче №3 можно вместо семафора применять мьютекс и периодически проверять переменную баланса, хватает ли денег.</p>



<p>Иногда может возникнуть задача, когда малое количество потоков или даже один изредка пишет в буфер, а множество потоков выполняют только доступ на чтение к этому буферу. Применение CV здесь не слишком эффективно, т.&nbsp;к. вы будете пробуждать все потоки (и записывающие и читающие) и самостоятельно реализовывать проверку возможности к действию. Мьютексы тоже не эффективны, если множество потоков постоянно осуществляют чтение блокируя доступ к буферу, то записывающий поток может ждать свою очередь очень долго. Или наоборот, множественная запись может парализовать чтение. Для таких ситуаций тоже существуют свои более эффективные сущности:</p>



<figure class="wp-block-table"><table><tbody><tr><td>Qt</td><td>C++</td></tr><tr><td>QReadWriteLock</td><td>std::shared_mutex</td></tr><tr><td>QReadLocker<br>QWriteLocker</td><td>std::shared_lock<br>std::lock_guard</td></tr></tbody></table></figure>



<p>Пример кода:</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: cpp; title: Пример кода с сайта https://zhitenev.ru; notranslate">
#include &lt;QCoreApplication&gt;
#include &lt;QThread&gt;
#include &lt;QDebug&gt;
#include &lt;QStringView&gt;
#include &lt;QReadWriteLock&gt;
#include &lt;QStringList&gt;
#include &lt;QWriteLocker&gt;
#include &lt;QReadLocker&gt;

QReadWriteLock lock;

QString msg { &quot;Hello &quot;};

class Transfer : public QThread {
    void run() override {
        QStringList list {&quot;Hi&quot;, &quot;Privet&quot;};
        for(int i = 0; i &lt; list.size(); ++i) {
            QThread::sleep(1);
            QWriteLocker locker(&amp;lock);
            msg = list.at(i);
        }
        QThread::sleep(1);
    }
public:
    Transfer(QObject* parent = nullptr) :
        QThread(parent)
    {

    }
};

class Receive : public QThread {
    QString name;
    void run() override {
        while(1) {
            QReadLocker locker(&amp;lock);
            qDebug() &lt;&lt; msg &lt;&lt; name;
            QThread::msleep(500);
        }
    }
public:
    Receive(QStringView name, QObject* parent = nullptr) :
        QThread(parent),
        name(name.toString())
    {

    }
};

int main(int argc, char *argv&#x5B;])
{
    QCoreApplication a(argc, argv);

    Receive t1(u&quot;Vasya&quot;);
    Receive t2(u&quot;Petya&quot;);

    Transfer w;

    t1.start();
    t2.start();
    w.start();

    QObject::connect(&amp;w, &amp;QThread::finished, &amp;t1, &amp;QThread::terminate);
    QObject::connect(&amp;w, &amp;QThread::finished, &amp;t2, &amp;QThread::terminate);

    w.wait();


    return a.exec();
}
</pre></div>


<p>и результат:</p>



<pre class="wp-block-preformatted">"Hello " "Vasya"
"Hello " "Petya"
"Hello " "Vasya"
"Hello " "Petya"
"Hello " "Vasya"
"Hi" "Vasya"
"Hi" "Petya"
"Hi" "Vasya"
"Hi" "Petya"
"Hi" "Petya"
"Hi" "Vasya"
"Privet" "Petya"
"Privet" "Vasya"
"Privet" "Vasya"
"Privet" "Petya"</pre>



<h2 class="wp-block-heading" id="Выводы">6. Выводы</h2>



<p>В целом нет жестких рекомендаций о том, какие сущности где и как применять. Здесь вы вольны сами выбирать подходящий вам инструмент. Однако, не забывайте про то, что за всё приходится платить. Всегда задавайте себе вопрос, а как часто будет пробуждаться поток, как часто он будет работать, а не гоняю ли я его зря? А может есть более дешевый способ выполнить эту работу? Всё это так или иначе приходит с опытом. Пробуйте, практикуйтесь и исследуйте! Всего доброго!</p><p>The post <a href="https://zhitenev.ru/thread-synchronize-and-safe-operation-with-global-data-at-multi-thread-environment-on-cpp-and-qt/">Синхронизация потоков и безопасная работа с общими данными в многопоточной среде на C++ и Qt</a> first appeared on <a href="https://zhitenev.ru">Блог Ильи Житенёва</a>.</p>]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>О Многопоточности в Qt и как создать поток</title>
		<link>https://zhitenev.ru/about-threading-in-qt-and-how-to-create-a-thread/</link>
		
		<dc:creator><![CDATA[Илья]]></dc:creator>
		<pubDate>Thu, 16 Sep 2021 17:08:00 +0000</pubDate>
				<category><![CDATA[C++ и Qt]]></category>
		<category><![CDATA[C++]]></category>
		<category><![CDATA[Qt]]></category>
		<category><![CDATA[Многопоточность]]></category>
		<guid isPermaLink="false">https://zhitenev.ru/?p=2816</guid>

					<description><![CDATA[<p>Многопоточность — свойство платформы выполнять код внутри одного процесса может выполнятся &#8220;параллельно&#8221; без предписанного порядка во времени. Такой подход полезен когда отделить ресурсоёмкую задачу от остального кода. Например, читать с диска в память большой файл и не «заморозить» графический интерфейс программы. В этой статье поговорим о многопоточности в C++ и о том, как это работает ... <a title="О Многопоточности в Qt и как создать поток" class="read-more" href="https://zhitenev.ru/about-threading-in-qt-and-how-to-create-a-thread/" aria-label="Больше на О Многопоточности в Qt и как создать поток">Читать далее</a></p>
<p>The post <a href="https://zhitenev.ru/about-threading-in-qt-and-how-to-create-a-thread/">О Многопоточности в Qt и как создать поток</a> first appeared on <a href="https://zhitenev.ru">Блог Ильи Житенёва</a>.</p>]]></description>
										<content:encoded><![CDATA[<p>Многопоточность — свойство платформы выполнять код внутри одного процесса может выполнятся &#8220;параллельно&#8221; без предписанного порядка во времени. Такой подход полезен когда отделить ресурсоёмкую задачу от остального кода. Например, читать с диска в память большой файл и не «заморозить» графический интерфейс программы. В этой статье поговорим о многопоточности в C++ и о том, как это работает во фреймворке Qt. На примере покажу как запустить в отдельном потоке только один метод объекта, оставив остальные методы «снаружи», обсудим плюсы и минусы такого подхода и как поступить в такой ситуации!</p>



<span id="more-2816"></span>



<pre class="wp-block-preformatted"><b>Содержание статьи</b>

1. <a href="#Как устроена многопоточность в Qt?" title="#Как устроена многопоточность в Qt?">Как устроена многопоточность в Qt?</a>
2. <a href="#Создание потока через обертку" title="#Создание потока через обертку">Создание потока через обертку</a>
3. <a href="#Переопределение метода run() класса QThread" title="#Переопределение метода run() класса QThread">Переопределение метода run() класса QThread</a>
4. <a href="#Достоинства и недостатки" title="#Достоинства и недостатки">Достоинства и недостатки</a></pre>



<div class="wp-block-buttons is-layout-flex wp-block-buttons-is-layout-flex"></div>



<h2 class="wp-block-heading" id="Как устроена многопоточность в Qt?">1. Как устроена многопоточность в Qt?</h2>



<p>В Qt потоками управляет класс QThread. Он представляет собой обёртку для потоков операционной системы и предоставляет кросс-платформенный интерфейс для работы с ними.</p>



<figure class="wp-block-pullquote"><blockquote><p> Помните, что один объект отвечает за один поток, поэтому не стоит бездумно плодить потоки. Создание потока сопряжено с существенными накладными расходами в программе. </p></blockquote></figure>



<p>Запустить код в отдельном потоке можно <a href="https://doc.qt.io/qt-5/qthread.html#details" target="_blank" rel="noreferrer noopener" title="https://doc.qt.io/qt-5/qthread.html#details">двумя способами</a>:</p>



<ol class="wp-block-list"><li>создание обёртки для нашего класса, который будет жить в отдельном потоке;</li><li>переопределение метода run() в унаследованном от QThread классе.</li></ol>



<figure class="wp-block-pullquote"><blockquote><p><strong>Важно!</strong> Выделение памяти оператором new для экземпляров класса необходимо выполнять в том потоке, в котором они будут исполнятся.</p></blockquote></figure>



<p>Это правило делает собственником объектов тот поток, который их создал. Так легче контролировать жизненный цикл объектов и позволит избежать ошибок в работе, когда используемый объект внезапно был удален в другом потоке.</p>



<h2 class="wp-block-heading" id="Создание потока через обёртку">2. Создание потока через обёртку</h2>



<p>Допустим, что вся тяжелая работа выполняется в методе doWork() класса HeavyWork, поэтому необходимо перенести его в отдельный поток. Код класса представлен ниже:</p>


<div class="wp-block-syntaxhighlighter-code alignwide"><pre class="brush: cpp; title: Пример кода с сайта https://zhitenev.ru; notranslate">
#include &lt;iostream&gt;
class HeavyWork : public QObject {
    Q_OBJECT
	public:
	    void doWork() {
	        std::cout &lt;&lt; &quot;Начинаем сложную работу&quot;;
      	    // Для эмуляции долгого выполнения сложной операции
	        // отправим поток в сон на 1 секунду
	        QThread::sleep(1);
	        std::cout &lt;&lt; &quot;Всё готово!&quot;;
	    }
	};
</pre></div>


<p>Экземпляр такого класса необходимо создать внутри потока. Напрямую сделать этого не возможно, поэтому создадим обработчик, который и будет создавать, взаимодействовать и уничтожать такой объект. Создаем класс Worker с одним методом и сигналом, излучаемым с флагом типа bool, оповещающем об (не) успешности выполненной работы. Все это происходит внутри метода process():</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: cpp; title: Пример кода с сайта https://zhitenev.ru; notranslate">
#include &lt;QObject&gt;
class Worker : public QObject {
    Q_OBJECT
    HeavyWork* work;
public slots:
    void process();
signals:
    void finished(bool);
};

void Worker::process() {
    // Этот метод будет запущен при старте потока
    // Аллоцируем наш объект. Теперь это происходит в отдельном потоке
    work = new HeavyWork();
    if(work == nullptr) {
        // Если произошла ошибка, то сигнализируем что поток завершен с отрицательным результатом и покидаем функцию (а с ней и завершается поток)
        emit finished(false);
        return;
    }
    // Делаем сложную работу
    work-&gt;doWork();
    // Сигнализируем об успешном выполнении
    emit finished(true);
}
</pre></div>


<p>На этом подготовительные операции выполнены и переходим к перемещению обработчика Worker в отдельный поток. Для этого создадим ещё один объект Controller, внутри которого вся магия и произойдет: экземпляр обработчика Worker и поток начнут взаимодействовать.</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: Пример кода с сайта https://zhitenev.ru; notranslate">
class Controller : public QObject {
    Q_OBJECT
public:
    void makeThread();
};
</pre></div>


<p>Единственный метод класса &#8211; makeThread(), вот он то нам и нужен, давайте его пошагово реализуем!</p>



<ol class="wp-block-list"><li>Создаем экземпляры обработчика Worker и экземпляр потока QThread</li></ol>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: cpp; title: Пример кода с сайта https://zhitenev.ru; notranslate">
Worker* worker = new Worker();
QThread* thread = new QThread();
</pre></div>


<p>2. Настроим и передадим данные, если нужно, экземпляру Worker. В этой точке у нас есть последний шанс сделать это привычным путем</p>



<p>3. Перемещаем worker в новорожденный поток</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: cpp; title: Пример кода с сайта https://zhitenev.ru; notranslate">
worker-&gt;moveToThread(thread);
</pre></div>


<p>Налаживаем связь между потоком и обработчиком. Ключ на старт!</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: Пример кода с сайта https://zhitenev.ru; notranslate">
// При запуске потока запускаем выполнение метода Worker::process()
connect(thread, &amp;QThread::started, worker, &amp;Worker::process);
// При излучении сигнала finished получаем флаг успешности и выводим в консоль соответствующее сообщение
connect(worker, &amp;Worker::finished, this, &#x5B;](bool state){
    if(state)
        std::cout &lt;&lt; &quot;Успех&quot; &lt;&lt; std::endl;
    else
        std::cout &lt;&lt; &quot;Хьюстон, у нас проблемы!&quot; &lt;&lt; std::endl;
});
// Также, по сигналу finished отправляем команду на завершение потока
connect(worker, &amp;Worker::finished, thread, &amp;QThread::quit);
// А потом удаляем экземпляр обработчика
connect(worker, &amp;Worker::finished, worker, &amp;QObject::deleteLater);
// И наконец, когда закончит работу поток, удаляем и его
connect(thread, &amp;QThread::finished, thread, &amp;QObject::deleteLater);
</pre></div>


<p>3..2..1.. Поехали! Запускаем выполнение потока</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: cpp; title: Пример кода с сайта https://zhitenev.ru; notranslate">
thread-&gt;start();
</pre></div>


<p>Реализация метода makeThread() завершена. Для проверки выполните следующий код и наблюдайте за стандартным выводом приложения:</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: Пример кода с сайта https://zhitenev.ru; notranslate">
Controller ctrl;
ctrl.makeThread();
</pre></div>


<h2 class="wp-block-heading" id="Переопределение метода run() класса QThread">3. Переопределение метода run() класса QThread</h2>



<p>Второй способ заключается в наследовании класса QThread и переопределении метода run(). <s>Однако, этот метод нарушает принципы SOLID в подавляющем большинстве случаев, поэтому пойдем окольными путями </s></p>



<pre class="wp-block-preformatted"><strong>Обновлено 23.02.22</strong>

Информация о нарушении принципов SOLID не совсем корректна. Подробнее об этом способе создания потоков я рассказал в статье <a href="https://zhitenev.ru/multithreading-in-qt-via-qthread-inheritance/" target="_blank" rel="noreferrer noopener" title="Многопоточность в Qt через наследование QThread">Многопоточность в Qt через наследование QThread</a></pre>



<p>Вместо наследования создадим свой класс и передадим один из его методов на выполнение в отдельный поток. В качестве нагрузки используем уже знакомый класс HeavyWork, а выполнять работу будем в классе AnotherController</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: cpp; title: Пример кода с сайта https://zhitenev.ru; notranslate">
class AnotherController : public QObject {
    Q_OBJECT
public:
    void makeThread();
};
</pre></div>


<p>, где метод makeThread() реализуем следующим образом:<br>1. Создаем экземпляр класса HeavyWork</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: cpp; title: Пример кода с сайта https://zhitenev.ru; notranslate">
HeavyWork* work = new HeavyWork();
</pre></div>


<p>2. Создаем поток через статический метод QThread::create() принимающий Function в качестве параметра. Function — это адаптер функциональных объектов. Создать адаптер можно с помощью функции std::bind из стандартной библиотеки шаблонов (STL). Этот адаптер будет запущен при запуске потока. Синтаксис следующий:</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: cpp; title: Пример кода с сайта https://zhitenev.ru; notranslate">
QThread* thread = QThread::create(std::bind(&amp;HeavyWork::doWork, work));
</pre></div>


<p>Первым аргументом будет указатель на метод doWork класса HeavyWork, а далее передаем контекст — указатель на конкретный класс, метод которого будет запущен. Нечто похожее проделывали в методе connnect для сигналов и слотов.<br><br>Далее через запятую указываются аргументы, если они нужны, которые будут переданы в метод doWork, но в нашем случае у метода doWork аргументы отсутствуют.</p>



<p>3. Налаживаем связь</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: cpp; title: Пример кода с сайта https://zhitenev.ru; notranslate">
connect(thread, &amp;QThread::finished, work, &amp;QObject::deleteLater);
connect(thread, &amp;QThread::finished, thread, &amp;Qobject::deleteLater);
</pre></div>


<p>В этом случае сигналов стало меньше, в виду того, что некоторые из них принадлежали объекту Worker, который теперь не используется, однако, ничего не мешает добавить сигналы в класс HeavyWork и связаться с ними.</p>



<p>4. И наконец запускаем выполнение потока.</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: cpp; title: Пример кода с сайта https://zhitenev.ru; notranslate">
thread-&gt;start();
</pre></div>


<p>Метод makeThread() реализован. Запуск программы аналогичен предыдущему случаю</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: cpp; title: Пример кода с сайта https://zhitenev.ru; notranslate">
AnotherController anCtrl;
anCtrl.makeThread();
</pre></div>


<p>Результат выполнения также аналогичен первому способу. В вашем распоряжении теперь два способа для запуска потока и мы можем обсудить их достоинства и недостатки.</p>



<h2 class="wp-block-heading" id="Достоинства и недостатки">4. Достоинства и недостатки</h2>



<ol class="wp-block-list"><li>Второй способ требует меньшего количества строк кода, и на один объект меньше. Он проще в реализации, но позволяет выполнять лишь один метод.</li><li>Поскольку только один метод находится в другом потоке в первом способе, то остальные методы могут быть использованы для управления тяжелым объектом из других потоков. Этот факт может выступать как достоинством, так и недостатком этого метода.</li><li>Например, при использовании семафоров или мьютексов во втором способе происходит блокировка всего потока, но т.&nbsp;к. в этом случае в отдельном потоке живет лишь один метод, то остальные могут быть вызваны в других потоках для модификации состояния объекта.</li><li>С другой стороны этот способ инкапсулирует мьютексы и другие управляющие элементы внутри одного объекта, позволяя управлять ими только через методы. В обоих случаях (3 и 4) программисту следует уделить много внимания такой структуре, т.&nbsp;к. в некоторых случаях можно заморозить другой поток, в котором вызывается блокирующий метод тяжелого класса.</li><li>Первый способ создает, выполняет и уничтожает тяжелый объект внутри себя. Такой подход более потокобезопасен т.&nbsp;к. жизненный цикл объектов не зависит от внешних потоков. Также весь объект целиком замирает в ожидании при засыпании потока и управление может быть осуществлено только через внешние объекты, ссылки на которые переданы в тяжелый метод.</li></ol>



<p>Исходя из выше написанного, можно заключить, что ни один из них не является панацеей и не даёт 100% гарантий, но «наломать дров» в первом способе чуть сложнее, чем во втором, если у вас большой и сложный объект. Однако, в случае когда у вас маленькая функция, можно смело использовать второй метод. Окончательное решение за выбор того или иного метода — за вами! Всего доброго!</p>



<p>==============</p>



<p>В следующей статье рассмотрим вопрос <a href="https://zhitenev.ru/thread-synchronize-and-safe-operation-with-global-data-at-multi-thread-environment-on-cpp-and-qt/" target="_blank" rel="noreferrer noopener" title="https://zhitenev.ru/thread-synchronize-and-safe-operation-with-global-data-at-multi-thread-environment-on-cpp-and-qt/">синхронизации потоков и разделения доступа к общим ресурсам</a>.</p><p>The post <a href="https://zhitenev.ru/about-threading-in-qt-and-how-to-create-a-thread/">О Многопоточности в Qt и как создать поток</a> first appeared on <a href="https://zhitenev.ru">Блог Ильи Житенёва</a>.</p>]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Как работает Git? Одно из частых заблуждений начинающих</title>
		<link>https://zhitenev.ru/kak-rabotaet-git-odno-iz-chastyh-zabluzhdenij-nachinajushhih/</link>
		
		<dc:creator><![CDATA[Илья]]></dc:creator>
		<pubDate>Tue, 12 May 2020 17:44:59 +0000</pubDate>
				<category><![CDATA[Новости]]></category>
		<category><![CDATA[git]]></category>
		<category><![CDATA[learning]]></category>
		<guid isPermaLink="false">https://zhitenev.ru/?p=2352</guid>

					<description><![CDATA[<p>Как часто при изучении чего-то нового вы пребывали в состоянии неопределённости, неясности синтаксиса и внутреннего устройства предмета изучения? Мозг буквально &#8220;скрипит и закипает&#8221; при попытке усвоить и уяснить материал. Однако, со временем эти процессы утихают по мере того, как вы погружаетесь в соответствующий материал. Тем не менее, существует важное заблуждение о работе Git, которое часто ... <a title="Как работает Git? Одно из частых заблуждений начинающих" class="read-more" href="https://zhitenev.ru/kak-rabotaet-git-odno-iz-chastyh-zabluzhdenij-nachinajushhih/" aria-label="Больше на Как работает Git? Одно из частых заблуждений начинающих">Читать далее</a></p>
<p>The post <a href="https://zhitenev.ru/kak-rabotaet-git-odno-iz-chastyh-zabluzhdenij-nachinajushhih/">Как работает Git? Одно из частых заблуждений начинающих</a> first appeared on <a href="https://zhitenev.ru">Блог Ильи Житенёва</a>.</p>]]></description>
										<content:encoded><![CDATA[<p class="lead">Как часто при изучении чего-то нового вы пребывали в состоянии неопределённости, неясности синтаксиса и внутреннего устройства предмета изучения? Мозг буквально &#8220;скрипит и закипает&#8221; при попытке усвоить и уяснить материал. Однако, со временем эти процессы утихают по мере того, как вы погружаетесь в соответствующий материал. Тем не менее, существует важное заблуждение о работе Git, которое часто встречается среди большинства новичков. Вот что, вероятно, они думают:</p>



<span id="more-2352"></span>



<blockquote class="wp-block-quote has-text-align-center is-style-large is-layout-flow wp-block-quote-is-layout-flow"><p>Git &#8211; это инструмент, хранящий изменения между файлами, верно?</p></blockquote>



<p>Неверно. Git не полагается на diff (инструмент, фиксирующий изменения между версиями файла). Если бы это было так, то при просмотре определённой версии проекта потребовалось бы вычисление всех изменений, внесённых во все файлы проекта с момента первого commit&#8217;а. А что если репозиторий имеет историю в 10 лет? Выходит весьма затратная операция.</p>



<p>И как же тогда быть, если надо быстро и &#8220;безболезненно&#8221; залезть в глубь репозитория?</p>



<h2 class="wp-block-heading">3 Столпа Git:</h2>



<div class="wp-block-image is-style-rounded"><figure class="aligncenter size-large is-resized"><img loading="lazy" decoding="async" src="https://zhitenev.ru/wp-content/uploads/2020/05/git-principles-1110x77.png" alt="" class="wp-image-2406" width="833" height="58" srcset="https://zhitenev.ru/wp-content/uploads/2020/05/git-principles-1110x77.png 1110w, https://zhitenev.ru/wp-content/uploads/2020/05/git-principles-730x50.png 730w, https://zhitenev.ru/wp-content/uploads/2020/05/git-principles-285x20.png 285w, https://zhitenev.ru/wp-content/uploads/2020/05/git-principles-768x53.png 768w, https://zhitenev.ru/wp-content/uploads/2020/05/git-principles.png 1406w" sizes="auto, (max-width: 833px) 100vw, 833px" /></figure></div>



<p>Таким образом, 99% вашей работы в Git это просто создание такого указанных выше объектов и манипуляции со ссылками между ними. Если ваш первый commit имеет следующую файловую структуру:</p>



<div class="wp-block-image"><figure class="aligncenter size-large"><img loading="lazy" decoding="async" width="204" height="121" src="https://zhitenev.ru/wp-content/uploads/2020/05/file-structure.png" alt="" class="wp-image-2410"/><figcaption>Ваш первый commit (файловая структура)</figcaption></figure></div>



<p>тогда Git сохранит это в следующей структуре:</p>



<div class="wp-block-image"><figure class="aligncenter size-large"><img loading="lazy" decoding="async" width="210" height="330" src="https://zhitenev.ru/wp-content/uploads/2020/05/git-objects-structure.png" alt="" class="wp-image-2411"/><figcaption>Ваш commit глазами Git. 3 директории и 3 файла, создают 3 tree-объекта и 3 blob-объекта и commit-объект.</figcaption></figure></div>



<p>Что же произойдёт, если мы внесем изменения в файл libs/base_libs/file.py и закомитим снова? При изменении файла Git создаст новый blob-объект, а также будет создан и новый tree-объект, потому что содержимое директории base_libs было также изменено, и её родительская директория тоже, выходит, была изменена. Таким образом, новый commit будет выглядеть так:</p>



<div class="wp-block-image"><figure class="aligncenter size-large"><img loading="lazy" decoding="async" width="364" height="379" src="https://zhitenev.ru/wp-content/uploads/2020/05/git-second-commit-object-structure.png" alt="" class="wp-image-2412" srcset="https://zhitenev.ru/wp-content/uploads/2020/05/git-second-commit-object-structure.png 364w, https://zhitenev.ru/wp-content/uploads/2020/05/git-second-commit-object-structure-285x297.png 285w" sizes="auto, (max-width: 364px) 100vw, 364px" /><figcaption>Это ваш второй commit. Поскольку мы изменили только одну ветвь дерева, то остальные объекты могут быть использованы повторно</figcaption></figure></div>



<p>Обратите внимание, что для неизменённых файлов используются указатели на объекты от предыдущего коммита. <strong>И второй и первый комит ссылаются на одни и те же объекты. Эта простая концепция и есть движущей силой Git! </strong>А что произойдёт если мы изменим settings.py и закомитим снова? Поскольку этот файл расположен в корневом уровне, то потребуется создать лишь один единственный blob-объект и родительский к нему tree-объект. Такая операция оставит абсолютно не тронутым ветвь &#8216;libs&#8217; директории, поэтому Git может использовать имеющиеся объекты снова.</p>



<div class="wp-block-image"><figure class="aligncenter size-large"><img loading="lazy" decoding="async" width="528" height="368" src="https://zhitenev.ru/wp-content/uploads/2020/05/git-commits-objects-structure.png" alt="" class="wp-image-2415" srcset="https://zhitenev.ru/wp-content/uploads/2020/05/git-commits-objects-structure.png 528w, https://zhitenev.ru/wp-content/uploads/2020/05/git-commits-objects-structure-285x199.png 285w" sizes="auto, (max-width: 528px) 100vw, 528px" /></figure></div>



<p>Используя этот подход, Git нет необходимости применять огромное количество итераций сравнения файлов для достижения нужного состояния репозитория в любой указанный момент времени. <strong>Образ вашего проекта в любой момент времени может быть восстановлен путём простого обхода дерева, начиная с commit-объекта. Теперь вы понимаете, что Git является объектно-ориентированным, а не diff-ориентированным.</strong></p>



<blockquote class="wp-block-quote has-text-align-center is-style-large is-layout-flow wp-block-quote-is-layout-flow"><p>Так Git абсолютно не использует сравнение версий?</p></blockquote>



<p>Не совсем. Поскольку Git пытается быть очень эффективным инструментом, когда дело доходит до хранения своих объектов на диске, а программные проекты могут очень быстро раздуваться, то Git сжимает ваши файлы и на этом он не останавливается. <em>Что произойдёт, если вы измените лишь одну строчку в огромном файле?</em> Исходя из того, что мы уже изучили, Git должен создать новый blob-объект. Получим 2 больших объекта в базе Git, которые очень похожи между собой. </p>



<p>Git учитывает такие инциденты и попытается создать файл, содержащий несколько объектов в одном файле. В этих объектных файлах Git хранит один файл полностью, а другой как дельту &#8211; разницу между второй версией и предыдущей. Полностью будет представлена более новая версия, т.к. наиболее вероятно, что вам понадобится именно она. Эта техника называется &#8220;дельта компрессия&#8221; и Git постоянно напоминает вам о ней, когда вы работаете с удалённым репозиторием.</p>



<div class="wp-block-image"><figure class="aligncenter size-large"><img loading="lazy" decoding="async" width="643" height="78" src="https://zhitenev.ru/wp-content/uploads/2020/05/git-delta-compression.png" alt="" class="wp-image-2416" srcset="https://zhitenev.ru/wp-content/uploads/2020/05/git-delta-compression.png 643w, https://zhitenev.ru/wp-content/uploads/2020/05/git-delta-compression-285x35.png 285w" sizes="auto, (max-width: 643px) 100vw, 643px" /><figcaption>Дельта компрессия &#8220;в действии&#8221;</figcaption></figure></div>



<p class="has-text-align-left">Таким образом, Git старается быть максимально эффективным.</p>



<h2 class="wp-block-heading">Подведём итоги</h2>



<ul class="wp-block-list"><li><strong>Git основан не на хранении изменений</strong> в версиях (diff), а на деревьях состояний;</li><li>Git <strong>не использует diff </strong>для представления версий проекта;</li><li>Git <strong>использует обход объектных-деревьев</strong> для представления версий;</li><li>Git <strong>использует diff</strong> для минимизации занимаемого дискового пространства.</li></ul>



<p></p><p>The post <a href="https://zhitenev.ru/kak-rabotaet-git-odno-iz-chastyh-zabluzhdenij-nachinajushhih/">Как работает Git? Одно из частых заблуждений начинающих</a> first appeared on <a href="https://zhitenev.ru">Блог Ильи Житенёва</a>.</p>]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Чему я научился за годы программирования: 12 советов разработчикам</title>
		<link>https://zhitenev.ru/chemu-ya-nauchilsya-za-gody-programmirovaniya-12-sovetov-razrabotchikam/</link>
		
		<dc:creator><![CDATA[Илья]]></dc:creator>
		<pubDate>Thu, 07 May 2020 17:45:34 +0000</pubDate>
				<category><![CDATA[Новости]]></category>
		<category><![CDATA[программирование]]></category>
		<category><![CDATA[разработка]]></category>
		<guid isPermaLink="false">https://zhitenev.ru/?p=2357</guid>

					<description><![CDATA[<p>Помню тот далекий день, примерно в 2005-2006 году, когда я будучи школьником, методом &#8220;проб и ошибок&#8221; пытался заставить работать свой первый скрипт. В конце-концов мне это удалось и я ощутил приятную радость и воодушевление. Так начинался мой путь разработчика. Весь этот путь я прошел практически один, у меня не было наставника или учителя, многое приходилось ... <a title="Чему я научился за годы программирования: 12 советов разработчикам" class="read-more" href="https://zhitenev.ru/chemu-ya-nauchilsya-za-gody-programmirovaniya-12-sovetov-razrabotchikam/" aria-label="Больше на Чему я научился за годы программирования: 12 советов разработчикам">Читать далее</a></p>
<p>The post <a href="https://zhitenev.ru/chemu-ya-nauchilsya-za-gody-programmirovaniya-12-sovetov-razrabotchikam/">Чему я научился за годы программирования: 12 советов разработчикам</a> first appeared on <a href="https://zhitenev.ru">Блог Ильи Житенёва</a>.</p>]]></description>
										<content:encoded><![CDATA[<p class="lead">Помню тот далекий день, примерно в 2005-2006 году, когда я будучи школьником, методом &#8220;проб и ошибок&#8221; пытался заставить работать свой первый скрипт. В конце-концов мне это удалось и я ощутил приятную радость и воодушевление. Так начинался мой путь разработчика. Весь этот путь я прошел практически один, у меня не было наставника или учителя, многое приходилось постигать самому, ошибаясь, осознавая это и возвращаясь обратно, пробуя пойти другим путём. Глядя на этот путь, я сформулировал некоторые важные вехи на этом пути и хочу поделиться ими.</p>



<span id="more-2357"></span>



<h2 class="wp-block-heading">1. Не изобретайте велосипед</h2>



<p>В современном мире идея открытого софта (Open-source) распространена достаточно широко. Не пользоваться этим богатым опытом других разработчиков весьма глупо.</p>



<p>Первое время, когда вы начинаете изучать что-то новое, реализовать все самому кажется хорошей идеей. Это действительно может быть полезно в образовательных целях. Однако, однажды вы можете столкнуться с неким вопросом, который поставит вас в тупик,  и хорошим решением будет поискать ответ у других &#8211; быть может кто-то уже решил этот вопрос ранее?</p>



<p>Сфокусируйтесь на реализации бизнес-логики вашей программы. Если вы хотите распарсить json-файл, не надо писать парсер с нуля &#8211; поищите сторонние библиотеки</p>



<h2 class="wp-block-heading">2. Выбирайте библиотеки разумно</h2>



<p>В продолжение предыдущего пункта, обязательно надо упомянуть о том, что хотя сегодня и существует огромное множество бесплатных библиотек на github, это ещё не значит что все они хорошего качества.</p>



<p>Обязательно проверяйте количество звезд, посмотрите другие проекты автора, убедитесь, что они тоже достойного качества, посмотрите тикеты (issues), как открытые, так и закрытые. Убедитесь в стабильности работы этого кода и обязательно загляните &#8220;под капот&#8221;, посмотрите сам код: как он написан, эффективен ли он, можно ли доверять ему?</p>



<h2 class="wp-block-heading">3. Расслабьтесь, выдохните, заварите чаю и читайте больше кода</h2>



<p>Программисты тратят много времени не на написание кода, а на его чтение. Если вы ещё не делаете так, то начните это делать!</p>



<p>Лучший способ научится хорошему коду &#8211; смотреть код лучших разработчиков. Постоянно изучайте чужую работу, посмотрите код ваших коллег, изучите их библиотеки. Возможно, вы найдёте и подчерпнете что-то новое для себя. </p>



<p>Этот совет особенно подходит для Open-source проектов, где весь код является полностью открытым и вы можете изучить всё до каждой мелочи. Погрузитесь в код, посмотрите как автор реализовал тот или иной функционал. </p>



<h2 class="wp-block-heading">4. Придерживайтесь стандартов и стилей</h2>



<p>Каждый программист имеет свой уникальный &#8220;почерк&#8221;, однако сегодня редко встретишь проекты, которые полностью созданы одним человеком и для одного человека. Скорее всего ваш код будут использовать другие люди. Сделайте его лёгким для прочтения и понимания. Позаботьтесь об удобстве не только других, но и о своём собственном. Ведь вернувшись к своему коду после долгого перерыва вы будете осваиваться в нём практически с нуля! Ваш код должен читаться легко, как поэма. Позвольте людям наслаждаться при чтении вашего кода.</p>



<h2 class="wp-block-heading">5. Думайте о безопасности с самого начала</h2>



<p>Некоторые программисты погружаются в проект с головой и начинают первым делом реализовывать бизнес-логику, при этом абсолютно не задумываясь о безопасности, потенциальных проблемах и возможных дырах. Этот процесс обычно откладывается на потом: &#8220;подумаю, об этом завтра!&#8221;. Однако, залатывать дыры и рефракторить код потом может быть очень затратным занятием. Думайте о безопасности программного обеспечения с первых строк.</p>



<h2 class="wp-block-heading">6. Продумайте архитектуру</h2>



<p>Как и вопросы безопасности вашего ПО, так и вопрос архитектуры является первостепенным. Вы будете бесконечно благодарны сами себе, если выберете верную архитектуру.</p>



<p>Разделяйте внешнее представление от логики, логику от данных. Применяйте методы SOLID и другие техники программирования для улучшения структуры проекта. Кто знает какие правки и какие добавки вы захотите сделать позже? Добавлять что-то в корявую структуру может быть невыносимо трудным занятием.</p>



<h2 class="wp-block-heading">7. Пользовательскому интерфейсу &#8211; особое внимание</h2>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow"><p>Пользовательский интерфейс &#8211; это как шутка, если вам приходится его объяснять, это плохо!</p><cite>Неизвестный автор</cite></blockquote>



<p>Если вы работаете в команде, то возможно у вас есть отдельный человек, в чью ответственность входит забота о UI\UX . Но если вы разработчик-одиночка, то не забывайте уделять внимание не только коду, но и дизайну.</p>



<p>Продумайте дизайн, зарисуйте его или в крайнем случае держите его в голове. Делайте простой, красивый и понятный интерфейс, который приятен глазу. Вы должны думать не как разработчик, а как простой пользователь, который будет использовать ваше ПО. </p>



<h2 class="wp-block-heading">8. Тестируйте, тестируйте и ещё раз тестируйте!</h2>



<p>Нет ничего более важного, чем тестирование! Тестируйте ваше ПО настолько тщательно, насколько это возможно. Потратьте время и разработайте обширные автотесты. Реализуйте различные стрессовые ситуации и посмотрите выживет ли ваша программа. </p>



<p>Вы можете сделать прекрасное ПО, но представьте, что пользователь в предвкушении запускает вашу программу, он в полном восторге от её эксплуатации и бац, программа вылетает с критической ошибкой. Ничего так не портит общее впечатление о программе и вашу репутацию как разработчика, как внезапные фатальные ошибки.</p>



<p>Не спешите делать релиз, не будучи уверенным в стабильности ПО. Потратьте дополнительный день, два, неделю, необходимое количество времени на тесты, чтобы убедится в стабильности вашего ПО.</p>



<h2 class="wp-block-heading">9. Начните использовать Git прямо сейчас!</h2>



<p>Если вы ещё не используете Git или любую другую систему контроля версий, то начните их использовать прямо сейчас!</p>



<p>Делать архивы вашего проекта и бэкапы, всякий раз, когда вы собираетесь внести изменения весьма занудно. Вы можете запросто забыть это сделать. А если вы внесли несколько изменений, весьма масштабных, и что-то стало работать не так. Как узнать об изменениях, внесённых в программу? Git позволит вам следить за изменениями в коде и в случае неприятностей откатиться назад.</p>



<h2 class="wp-block-heading">10. Не гонитесь за новинками</h2>



<p>Все любят современное и мощное оборудование. Будучи разработчиком помните, что не все ваши пользователи могут быть счастливыми обладателями мощных и современных компьютеров, технологий. В такой ситуации, ваше ПО, которое прекрасно себя ведет на новинках из мира ПО и железа, может быть абсолютно непригодным для использования в старых версиях ОС, фреймворков или на слабом железе.</p>



<h2 class="wp-block-heading">11. Изучите шаблоны проектирования</h2>



<p>Разрабатывая сложный и крупный проект вы будете сталкиваться лицом к лицу с некоторыми распространёнными проблемами, которые уже были решены до вас кем-то другим, более компетентным и эти типовые решения &#8211; получили названия шаблоны проектирования (design patterns).</p>



<p>Изучив их, вы заметите, что многие проблемы архитектуры имеют то самое типовое решение, описываемое одним из шаблонов. Поэтому потратьте своё время на их изучение!</p>



<p>Это инвестиция, которая будет давать вам &#8220;дивиденды&#8221; вечно!</p>



<h2 class="wp-block-heading">12. Настало время отдавать!</h2>



<p>На пути программиста мы часто сталкиваемся с какими-либо проблемами и находим решение в сети Интернет. Пришло время возвращать!</p>



<p>Однажды, создавая сложный проект, вы заходите на Gtihub и находите несколько библиотек, которые решают многие из ваших проблем, и позволяют вам сосредоточится на самом главном, сэкономить время и силы. Это стало возможным благодаря чужому труду, благодаря тому, что кто-то создал данный инструмент, скомпилировал его, протестировал и опубликовал для дальнейшего распространения, чтобы сделать сообщество лучше!</p>



<p>Вспомните тот день, когда вы наглухо застряли с каким-либо багом или проблемой, или ввязались во что-то новое и не знаете куда двигаться дальше. Что происходит в таком случае? Вы идёте в поисковую систему и находите чей-то пример, проект, пост в блоге, который помогает вам преодолеть трудности здесь и сейчас. Это стало возможным благодаря тому, что кто-то отложил свои дела, вечерний просмотр фильма или встречу с друзьями в выходной день, чтобы написать тот или иной пост в блог.</p>



<p>Настало и ваше время, отдавать на благо сообщества. Чем больше вы отдадите, тем больше получите обратно. Все мы очень заняты и погружены в работу, что очень трудно найти свободную минутку, чтобы сделать что-то для других. Но постарайтесь найти свободный час хотя-бы раз в неделю, чтобы поделиться своим опытом или наработками и сделать сообщество программистов богаче!</p>



<hr class="wp-block-separator"/>



<p>В этой статье я попытался собраться мыслями и сформулировать некие советы для разработчиков разного ранга. Кто-то нашел здесь что-то новое для себя, а кому-то статья напомнит о чем-то давно известном, но временно вылетевшим из головы, кто-то сможет чем-то дополнить эту статью и поделиться своим опытом. Я продолжаю свой путь, продолжаю развитие и изучение, и буду продолжать делиться своим опытом. Я надеюсь, что этот пост сделал чью-то жизнь чуточку легче.</p><p>The post <a href="https://zhitenev.ru/chemu-ya-nauchilsya-za-gody-programmirovaniya-12-sovetov-razrabotchikam/">Чему я научился за годы программирования: 12 советов разработчикам</a> first appeared on <a href="https://zhitenev.ru">Блог Ильи Житенёва</a>.</p>]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Менеджер контекста with в Python</title>
		<link>https://zhitenev.ru/menedzher-konteksta-with-v-python/</link>
		
		<dc:creator><![CDATA[Илья]]></dc:creator>
		<pubDate>Sat, 28 Dec 2019 15:46:00 +0000</pubDate>
				<category><![CDATA[Без категории]]></category>
		<category><![CDATA[python]]></category>
		<category><![CDATA[Практика]]></category>
		<guid isPermaLink="false">https://zhitenev.ru/?p=2339</guid>

					<description><![CDATA[<p>Инструкция with в языке Python часто заводит в тупик тех, кто только начинает своё знакомство с  этим языком. Менеджер контекста, создаваемый инструкцией with, таит в себе невиданную мощь и очень полезный функционал, позволяющий безопасно использовать ресурсы, требуемые получения и обязательного освобождения(завершения). Однако, творческий подход позволяет с удобством использовать этот инструмент и в других областях. Как ... <a title="Менеджер контекста with в Python" class="read-more" href="https://zhitenev.ru/menedzher-konteksta-with-v-python/" aria-label="Больше на Менеджер контекста with в Python">Читать далее</a></p>
<p>The post <a href="https://zhitenev.ru/menedzher-konteksta-with-v-python/">Менеджер контекста with в Python</a> first appeared on <a href="https://zhitenev.ru">Блог Ильи Житенёва</a>.</p>]]></description>
										<content:encoded><![CDATA[<p class="lead">Инструкция with в языке Python часто заводит в тупик тех, кто только начинает своё знакомство с  этим языком. Менеджер контекста, создаваемый инструкцией with, таит в себе невиданную мощь и очень полезный функционал, позволяющий безопасно использовать ресурсы, требуемые получения и обязательного освобождения(завершения). Однако, творческий подход позволяет с удобством использовать этот инструмент и в других областях. </p>



<span id="more-2339"></span>



<h2 class="wp-block-heading" id="kak-eto-rabotaet">Как это работает?</h2>



<p> Рассмотрим задачу записи данных в последовательный порт. Для решения этой задачи с применением контекста используют следующий код: </p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: python; title: Пример кода с сайта https://zhitenev.ru; notranslate">
import serial
with serial.Serial(port=&#039;COM3&#039;) as s:
          s.write(&#039;Привет&#039;)
          s.write(&#039;Как дела&#039;)
</pre></div>


<p> Здесь мы определяем псевдоним s и используем его в контексте serial. В теле контекста может располагаться любой блок кода, в т.ч и другие контексты. Применение менеджера контекста позволяет гарантировать, что ресурс Serial будет закрыт, что удобно, например, в многопоточных приложениях, где ресурс надо быстро освобождать для возможности использования другими потоками. Или банальная ошибка программиста &#8211; забыть закрыть метод, тоже решается использованием менеджера контекста. </p>



<h2 class="wp-block-heading" id="zachem-eto-nuzhno">Зачем это нужно?</h2>



<p>В приведённом выше примере вам не нужно вызывать функции open и close объекта serial, потому что их за вас вызовет менеджер контекста. То есть, вам не нужно беспокоится о закрытии порта после выполнения некоторых действий &#8211; это сделается автоматически.</p>



<p>Казалось бы совсем не сложно контролировать процесс освобождения ресурса самостоятельно, написав следующее:</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: Пример кода с сайта https://zhitenev.ru; notranslate">
import serial

s = serial.Serial(port=&#039;COM3&#039;)
s.open()
s.write(&#039;Привет&#039;)
s.write(&#039;Как дела?&#039;)
s.close()
</pre></div>


<p>Однако, есть тут один нюанс. <strong>Если вдруг где-то между открытием и закрытием порта произойдёт исключение (exception), то закрытие ресурса не гарантируется! </strong></p>



<p>Конечно, вы можете избежать такой ситуации и  модифицировать код следующим образом:</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: python; title: Пример кода с сайта https://zhitenev.ru; notranslate">
import serial

s = serial.Serial(port=&#039;COM3&#039;)
s.open()
try:
    s.write(&#039;Привет&#039;)
    s.write(&#039;Как дела?&#039;)
finally:
    s.close()
</pre></div>


<p>И да, этот код будет абсолютно функционально эквивалентен применению менеджера контекста, он также будет безопасен, но вы можете сделать его проще и лаконичнее с помощью with.</p>



<p>Главная ценность инструкции with это возможность абстрагироваться от функциональности распространенных шаблонов управления ресурсов, упрощая работу с ними. </p>



<h2 class="wp-block-heading" id="kak-eto-ispolzovat-v-sobstvennyh-razrabotkah">Как это использовать в собственных разработках?</h2>



<p>Возникает закономерный вопрос: “Как инструкция with узнает, какой метод вызывать при открытии контекста и при его завершении”?</p>



<p>Для этого используются “волшебные” методы __enter__ и __exit__ или декоратор, созданный с помощью модуля contextlib. Рассмотрим оба случая.<br></p>



<h3 class="wp-block-heading" id="sobstvennyi-klass">Собственный класс</h3>



<p>Метод __enter__ выполняется при входе в контекст, а метод __exit__ при выходе из него. Кроме безопасного доступа к ресурсам, этот функционал можно использовать для аккуратной реализации не связанных с ресурсами вещей, например, оформления вывода текста.</p>



<p>Давайте разберем на примере: напишем программу, которая будет решать творческую задачу через контексты: формировать маркированный список с разной степенью вложенности.</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: python; title: Пример кода с сайта https://zhitenev.ru; notranslate">
class list:
	def __init__(self):
		self.level = 0

	def __enter__(self):
		self.level += 1
		return self

	def __exit__(self, exc_type, exc_val, exc_tb):
		self.level -= 1

	def write(self, text):
		print(&#039;    &#039; * self.level + &#039;* &#039; + text)


with list() as l:
	l.write(&quot;Фрукты&quot;)
	with l:
		l.write(&quot;Бананы&quot;)
		l.write(&quot;Ананасы&quot;)

	l.write(&quot;Ягоды&quot;)
	with l:
		l.write(&quot;Черника&quot;)
		l.write(&quot;Малина&quot;)
</pre></div>


<p>И в результате выполнения программы мы получим следующий отформатированный список с двумя уровнями вложенности:</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: Пример кода с сайта https://zhitenev.ru; notranslate">
    * Фрукты
        * Бананы
        * Ананасы
    * Ягоды
        * Черника
        * Малина
</pre></div>


<h3 class="wp-block-heading" id="decorator">Декоратор</h3>



<p>Декоратор можно использовать когда создавать целый класс накладно, а вот одна функция вас вполне бы устроила. Давайте напишем функцию, которая предоставляет возможность безопасного использования последовательного порта: получая и освобождая ресурс. В помощь нам приходит библиотека contextlib<br></p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: python; title: Пример кода с сайта https://zhitenev.ru; notranslate">
from contextlib import contextmanager
import serial

@contextmanager
def comOpen(portname):
    ser = serial.Serial()
    try:
        ser.setPort(portname)
        yield ser
    except serial.serialutil.SerialException:
        print(&quot;Ошибочка вышла!&quot;)
    finally:
        print(&#039;Закрываем порт&#039;)
        ser.close()


with comOpen(&#039;COM3&#039;) as s:
    s.write(&#039;Hello!&#039;)
</pre></div>


<p>И если мы выберем заранее неверный порт, то увидим на экране вывода сообщение:</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: Пример кода с сайта https://zhitenev.ru; notranslate">
Ошибочка вышла!
Закрываем порт
</pre></div>


<p>Реализация удалась! Менеджер контекста работает!</p>



<h2 class="wp-block-heading" id="rezume">Резюме</h2>



<ul class="wp-block-list"><li>Менеджер контекста определяется инструкцией with и позволяет полностью абстрагироваться от управления ресурсами и гарантировать их освобождение при выходе из контекста</li><li>Вы можете создавать собственные объекты, которые можно применять с менеджерами контекста</li><li>Используя вложенность контекстов и творческих подход, можно использовать их для решения задач не связанных с владением и управлением ресурсами. Например, реализовать форматирование текста в виде список с разными уровнями вложенности</li></ul><p>The post <a href="https://zhitenev.ru/menedzher-konteksta-with-v-python/">Менеджер контекста with в Python</a> first appeared on <a href="https://zhitenev.ru">Блог Ильи Житенёва</a>.</p>]]></content:encoded>
					
		
		
			</item>
	</channel>
</rss>
