Не так давно на базе нашего офиса мы провели бесплатный курс лекций «Современный и эффективный С++» и записали их на видео. Курс был рассчитан на недавно прибывших в наш коллектив программистов, стажеров и всех желающих. В этой статье мы хотели бы осветить цель данного курса, процесс подготовки к нему, подвести итоги. Надеемся, что для кого-то из вас наш курс лекций будет интересен и вы оцените наш труд. Желаем приятного просмотра докладов!


Как пришла идея


Наша компания PVS-Studio занимается разработкой статического анализатора кода, который выявляет ошибки и потенциальные уязвимости в исходном коде программ, написанных на С, С++, С# и Java. И ни для кого не секрет, что мы заинтересованы в привлечении молодых специалистов. Очень часто бывает так, что у нас проходят практику студенты, которые хотели бы получить профессиональный опыт в сфере программирования и которым пока не хватает знаний в этой области. Кроме того, нашим недавно прибывшим специалистам также нужна помощь в профессиональном росте. Ввиду вышеизложенного было принято решение подготовить свой курс лекций по программированию и провести его. Данная идея преследовала как минимум три цели: показать молодым специалистам и стажерам, как можно писать код аккуратнее, эффективнее и безопаснее; популяризовать С++ за пределами офиса и продемонстрировать, что язык перспективен с точки зрения изучения и дальнейшей работы с ним; популяризовать нашу компанию в качестве интересного работодателя. Определившись с целями и воодушевившись идеей, мы приступили к воплощению мечты в реальность.

Подготовка


При дальнейшем обсуждении нашей затеи стало появляться так много вопросов и так мало ответов (удивительно, да?). На повестке дня стояли: время и место проведения, кто подготовит курс, кто его проведет, какие темы должны быть освещены, будем ли мы записывать курс на видео, как мы будем это делать, а как продвигать, а где это лучше сделать и еще огромное количество вопросов, которые мы обсуждали еще недели две и повторяли как галчата со словами: «А как...?», «А кто...?», «А куда...?».

И вот, когда мысли и идеи у всех «синхронизировались», было решено, что курс будет рассчитан на 12 лекций по 2 раза в неделю с 9 июля по 15 августа. В качестве лекторов были выбраны Андрей Карпов и в добровольно-принудительном порядке Филипп Хандельянц.

На нашем канале YouTube мы собрали все видео в отдельный плейлист для удобного просмотра. Также у нас есть группа Вконтакте, где собрана вся информация по лекциям. Таким образом, можно было зайти на страничку отдельной лекции, посмотреть, о чем она, зарегистрироваться на нее, а позже также посмотреть запись лекции, если что-то захотелось повторить или если человек не смог присутствовать лично.

Давайте подробнее остановимся на темах лекций и их содержании.

Нововведения стандарта С++11


С++11… как много смысла в этих пяти символах… Первая лекция получилась самой длинной и, пожалуй, информативной, поскольку почти за два часа Филипп с ребятами обсудил немало вопросов. Так они начали с рассмотрения языка С++, актуальности его использования. Затем перешли непосредственно уже к самим изменениям в стандарте С++11. В ходе этой части рассматривалось расширение ядра языка, а именно:

  • универсальная инициализация;
  • move-семантика;
  • обобщенные константные выражения (constexpr);
  • изменения в определении POD-типа;
  • списки инициализации (std::initializer_list<T>);
  • вывод типов через auto и decltype;
  • диапазонный for;
  • лямбда-функции;
  • альтернативный синтаксис функций;
  • вывод возвращаемого типа функции;
  • делегирующие конструкторы;
  • спецификаторы default/delete для функций;
  • спецификаторы override/final при работе с полиморфизмом;
  • nullptr_t и nullptr;
  • и многое, МНОГОЕ другое.

После обсуждения расширения ядра Филипп рассказал про расширения стандартной библиотеки, которые включают в себя:

  • обновление стандартной библиотеки с учетом изменений ядра языка;
  • многопоточность: потоки (std::thread), мьютексы (std::mutex, ...), условные переменные (std::conditional_variable, ...);
  • RAII-обертки (std::lock_guard, std::unique_lock);
  • кортежи (std::tuple);
  • хэш-таблицы (std::unordered(_set/_multiset/_map/_multimap);
  • регулярные выражения (std::regex);
  • умные указатели (std::unique_ptr, std::shared_ptr, std::weak_ptr);
  • генераторы псевдослучайных чисел вместо rand();
  • обертки ссылок (std::reference_wrapper);
  • обобщенная обертка для функций (std::function);
  • типажи (type_traits).

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


Нововведения стандарта С++14


Вторая лекция была посвящена рассмотрению нововведений стандарта С++14. Изменений было не так много, поэтому слушатели «страдали» уже не 2 часа, а всего лишь 40 минут :-). Как и на первой лекции, внимание уделялось двум основным моментам: изменению ядра С++ и изменению стандартной библиотеки.

Вот о чем поговорили:

  • вывод возвращаемого типа для функций без явного указания завершающего объявления возвращаемого типа (trailing return type);
  • decltype(auto);
  • ослабление ограничений для constexpr;
  • вариативные переменные;
  • агрегатную инициализацию;
  • бинарные литералы;
  • разделители числа;
  • обобщенные лямбды;
  • список захвата лямбд функций с инициализацией;
  • атрибут [[deprecated]], std::shared_timed_mutex;
  • гетерогенный поиск для ассоциативных контейнеров;
  • стандартные пользовательские литералы (s для std::string; h, min, s, ms, us, ns для std::duration, if, i, il для мнимой части std::complex);
  • std::get теперь может достать значение из std::tuple по типу;
  • std::make_unique;
  • std::integer_sequence;
  • свободные функции std::begin, std::end, std::cbegin, std::cend, std::rbegin, std::rend, std::crbegin, std::crend;
  • std::exchange.


Вывод типов в C++


Осветив нововведения стандартов С++11 и С++14, мы остановились на теме вывода типов в C++. Зачем было затрагивать данную тему? До C++11 мало кто основательно задумывался, что это такое и как это работает, исключение — писатели «шаблонной магии» и разработчики компиляторов.

Времена проходят, и теперь уже C++ программисту необходимо учитывать принятые стандартами нововведения: auto, forwarding-ссылки, decltype, список захвата лямбд, вывод типа по возвращаемому значению и т.д.

Нововведения начали активно применяться разработчиками в свежем коде, и очень важно понимать, что же это означает, и что сделает в этой ситуации компилятор.


Нововведения стандарта С++17


Говоря о современном С++, нельзя было не упомянуть последний существующий стандарт C++. Повествование было изложено по уже зарекомендовавшей себя схеме: рассмотрены изменения в ядре, а затем в стандартной библиотеке. Таким образом, рассмотрели:

  • вывод типов шаблонных классов из конструкторов;
  • throw() считается как noexcept и помечен как depricated;
  • noexcept стал частью системы типов и теперь учитывается при выборе перегрузки функции;
  • copy elision;
  • свертка (fold expression);
  • if/switch с инициализатором;
  • if constexpr, structured bindings;
  • вложенные пространства имен;
  • новые атрибуты [[fallthrough]], [[maybe_unused]] и [[nodiscard]];
  • шестнадцатеричные вещественные литералы;
  • static_assert принимает сообщение как опциональный параметр вместо обязательного;
  • typename теперь разрешен в template template параметрах;
  • очередное новое правило для вывода типа auto с brace-initializer-list;
  • inline переменные;
  • auto можно использовать в параметрах шаблона;
  • препроцессорная директива __has_include;
  • register стало зарезервированным словом и пока не используется;
  • std::string_view;
  • std::optional;
  • std::any;
  • std::variant;
  • std::byte;
  • std::uncaught_exceptions вместо std::uncaught_exception;
  • boost::filesystem с изменениями залит как std::filesystem;
  • try_emplace и insert_or_assign для ассоциативных массивов;
  • свободные функции std::size, std::empty и std::data;
  • шаблонные переменные для type_traits (std::*_v<T...>).


STL: концепция, контейнеры, итераторы


Пятую лекцию целиком и полностью посвятили стандартной библиотеке шаблонов. Обсудили историю STL, из чего она состоит и как начать использовать. Более детально поговорили о стандартных контейнерах (vector, list, forward_list, deque, ...), контейнерах-адаптерах (stack, queue, priority_queue, heap) и о типах итераторов (input, output, forward, bidirectional, random, contiguous).


STL: алгоритмы, обертки, функциональные объекты


На этой лекции продолжили работать над STL. Филипп рассказал про то, что не надо заниматься велосипедостроительством и пора начать использовать стандартные алгоритмы STL. Были рассмотрены различные категории алгоритмов, полезные функциональные обертки (plus, minus, ...).


Статический анализ как неотъемлемая часть разработки при написании программ на С++


Филипп решил немного отдохнуть :-), и седьмую лекцию провел наш технический директор — Андрей Карпов. Он рассказал про статический анализ в общем, и почему необходимо его использовать в работе. Андрей объяснил прямую взаимосвязь между использованием статического анализатора и улучшением качества кода при работе над большим проектом. Было бы глупо рассказывать про статический анализ без примеров из нашей рабочей действительности, поэтому, естественно, примеры обнаруживаемых ошибок PVS-Studio также были включены в лекцию. Поговорили о правильных и неправильных способах использования инструментов анализа кода.


Стандарт кодирования PVS-Studio и общие приемы при разработке эффективных С++ диагностик


Филипп по-прежнему отдыхает, а Андрей со слушателями заглянули немного на внутреннюю кухню разработки PVS-Studio, рассмотрев две темы.

Во-первых, поговорили немного о стандарте кодирования, принятом в нашей команде, и почему мы решили оформлять код именно так, а не иначе.

Во-вторых, обсудили некоторые приемы микрооптимизаций кода, которые мы используем при разработке анализатора. Подискутировали насчет знаменитой фразы «premature optimization is the root of all evil». Тема была затронута вскользь, но Андрей вскоре планирует посвятить этому отдельный доклад "Преждевременная оптимизация — зло! Да здравствует преждевременная оптимизация!" на конференции С++Russia. Так что приглашаем всех желающих на эту замечательную конференцию и доклад Андрея.

Лекция в первую очередь была ориентирована на стажёров нашей компании и тех, кто со временем хочет ими стать. Касательно содержания, то здесь обсудили такие моменты стандарта кодирования PVS-Studio:

  • общие положения;
  • строки;
  • именование переменных;
  • именование типов;
  • именование функций;
  • выравнивание кода;
  • табличное оформление сложного условия;
  • разделяющие пробелы;
  • преждевременная оптимизация;
  • оптимизация размеров классов и структур;
  • переменные с коротким временем жизни и другое.


Метапрограммирование в С++: шаблоны, constexpr-вычисления и т.д.


Филипп отдохнул, набрался сил и рассказал за «шаблонную магию». Всем известно, что метапрограммирование с каждым выходом нового стандарта становится все сложнее и непонятнее, в кодовых базах количество «шаблонной магии» непрерывно растет. Лекция ставила задачу ввести в курс дела, рассказав об основных аспектах метапрограммирования:

  • шаблонные функции;
  • шаблонные классы;
  • полная и частичная специализация шаблонов;
  • вариативные шаблоны;
  • свертка.

Слушатели спустя 30 минут:


Но это было еще не все! Филипп решил добить их окончательно рассказать про вычисления и манипуляции с типами на этапе компиляции, о паттерне CRTP (Curiously recurring template pattern), SFINAE и детекторах.


Сборка С/С++ проектов и ее оптимизация


Выжившим после предыдущей лекции было рассказано, как пить меньше кофе и меньше сражаться на мечах, пока компилируется их проект.


Были затронуты следующие аспекты:

  • фазы трансляции в языках C/C++;
  • зависимости;
  • внешние шаблоны;
  • предкомпилированные заголовки;
  • параллельная и распределенная компиляция;
  • кэш компилятора;
  • замена компонентов трансляции: препроцессор, компилятор, компоновщик;
  • Single compilation unit (SCU);
  • модули.


Неопределенное поведение, или как выстрелить себе в ногу


«Некоторые думают, что они знают, как ведёт себя язык C и C++, когда они играют с переполнением или нулевыми указателями. Нет, они не знают. И никто не знает. Здесь начинается неопределённое поведение, и слово „неопределённое“ означает, что мы не можем строить никаких предположений и догадок. Просто нельзя писать программы, в которых возникает неопределённое поведение, и точка. Рассмотрим тему неопределённого поведения и некоторые примеры кода, приводящие к нему», — примерно так звучало начало 11 лекции, на которой Андрей снова принял эстафету у Филиппа и осветил ключевые моменты данной темы, включая терминологию:

  • неопределённое и неуточнённое поведение;
  • выход за границу массива;
  • отсутствие виртуального деструктора;
  • оператор delete;
  • нулевые указатели;
  • сдвиги;
  • целочисленное переполнение (64-битные ошибки);
  • целочисленное переполнение (контрольная сумма).


С++20 и обозримое будущее


Мы не могли не завершить наш цикл лекций рассказом о скором выходе самого последнего стандарта С++20. Основной акцент был сделан на концептах (Concepts), библиотеке диапазонов (Ranges), сопрограммах (Coroutines), модулях (Modules), контрактах (Contracts), операторе трехстороннего сравнения operator<=> (Spaceship operator), макросах для тестирования функциональности (Feature test macros), атрибутах (Attributes), std::format, std::jthread и на многом другом…


И вот, когда все организационные моменты были урегулированы, наступил момент реализации идеи.

Реализация


Мы бы, конечно, назвали эту стадию «Ожидание-Реальность», потому что, как это обычно бывает, всё, что задумывается на этапах планирования, меняется еще миллион раз на этапе проведения. Естественно, мы осознавали этот факт и были готовы (нет), что что-то пойдет не так. Основной проблемой для нас стали соседи снизу, которые активно ремонтировали арендуемое ими помещение, и в нашем офисе наступило дрельно-перфораторное время (без комментариев), которое не могло не отразиться на записи лекций. В связи с этим, практически половина лекций из курса перезаписана заново или отсняты некоторые кусочки видео. И если в случае с Андреем перезаписывать ничего не приходилось, так как его лекции продолжались около часа и нам хватало этого времени, чтобы компания снизу не проводила монтажные работы, то с лекциями Филиппа пришлось помучиться, ибо 1,5-2 часа на одну лекцию не каждый программист выдержит и не каждый рабочий в состоянии столько выжидать, не работая. Пожалуй, эта ситуация была самой непредвиденной, в остальном каких-либо серьезных проблем не возникало.

А теперь поговорим о начале курса. На первую лекцию к нам записалось порядка 20 участников, а фактически дошло 11 слушателей. Конечно же мы понимали, что судить по первому мероприятию глупо, и решили понаблюдать, что будет дальше. Численность слушателей за весь курс постоянно менялась: то увеличивалась, то уменьшалась. Однако нам понравился тот факт, что образовался некий «костяк» группы, который присутствовал на каждой лекции. Из плюсов можно также выделить активность участников. Как и в любом новом коллективе, изначально все смущались и молча слушали лекцию, но уже где-то с 3-4 лекции прошла эта скованность и занятия протекали в более активной форме — вопросы, обсуждения, просто живое общение после лекции — и были случаи, когда человек продолжал общаться с лектором еще минут 40-50, потому что ему очень интересна тема и он действительно хочет развиваться в этом направлении. Это не могло не радовать, так как мы понимали, что отчасти мы этому факту поспособствовали (+100 к карме).


Самым стойким были вручены сертификаты.


Итоги


Ключевым показателем эффективности нашей затеи является достижение поставленных целей. Стало ли легче работать молодым специалистам и стажерам в нашей команде с точки зрения социализации и улучшения профессиональных навыков и умений? Однозначно — да, так как в основу лекций были заложены конкретные примеры, с которыми сталкиваются наши программисты во время работы. Кроме того, подобная форма обучения способствовала ускорению процесса адаптации в коллективе для новых сотрудников. Смогли ли мы популяризовать язык С++ за пределами офиса? Откровенно говоря, думаем, что тут 50/50. С одной стороны, были очень заинтересованные люди, которые не только ходили на наши лекции, но и пересматривали их потом, искали дополнительную информацию по той или иной теме и приходили во всеоружии уже на следующую лекцию. Да что там подготовка! Был случай, когда шел очень сильный дождь, и человек пришел насквозь мокрый, чтобы не пропустить занятие. Вот это я понимаю — рвение! С другой стороны, желающих посетить наш курс «извне» изначально было больше, чем под конец. На это у меня есть как минимум три обоснования:

  1. Основой ЦА были студенты, и, возможно, записавшись на курс, они имели определенное представление по теме, но их ожидания не совпали с реальностью, так как, несмотря на то, что курс охватывал общие моменты программирования на языках С/С++, было очень много узконаправленных и специализированных моментов.
  2. Курс проходил в разгар лета (да-да, знаем, что в этом году это не аргумент, особенно, если вы живете в средней полосе России). Многие попросту уехали из города и не смогли посещать курс, даже если были в нем заинтересованы.
  3. Были тру-программисты, которые итак всё знают и без нашего курса :)

В заключение хотелось бы сказать, что идею создания собственного небольшого курса по программированию считаем успешной, поскольку этот опыт помог нашей компании оптимизировать некоторые внутренние процессы работы и показать наглядно наши стандарты будущим соискателям.

Если у вас остались какие-либо вопросы, может быть, даже предложения или идеи, то смело оставляйте комментарии — пообщаемся :). И да, спасибо, что прочитали эту статью.

Ссылка на подборку докладов: «Современный и эффективный С++» Вконтакте и YouTube. Будем благодарны, если поделитесь ими в социальных сетях.

Комментарии (14)


  1. ukt
    01.10.2019 22:44
    -1

    Один из замечательнейших языков, а то всё веб, да веб. С/С++!

    А не было мысли сделать курс в виде интерактива, как степик или подобные?


    1. Andrey2008
      01.10.2019 23:12

      Нет. Зато бродят мысли организовывать своего рода платные курсы повышения квалификации по теме «Написание качественного кода на языке Си и Си++». Есть ли здесь менеджеры/тимлиды, которые бы отправили программистов на подобные мероприятия?


      1. Sazonov
        04.10.2019 19:57
        +1

        Это очень невыгодно для многих аутсорсинговых/аутстаффинговых компаний. Сначала тратятся деньги на курсы, потом поумневшие программисты начинают просить больше денег. А на суммы в контрактах с заказчиком так просто не изменишь. (Это юмор, если что)
        Но лично я бы некоторых коллег отправил.


        1. faoriu
          05.10.2019 10:50

          Почему бы не прописывать обязательства сторон в контракте: хочешь обучение — подписывай доп. соглашение, в котором прописаны выгоды и обязательства сторон прямо.


    1. slonopotamus
      01.10.2019 23:41

      С/С++

      Но это два разных, несовместимых языка, с разной идеологией и возможностями.


      1. ukt
        02.10.2019 19:17

        Я где то утверждал обратное?


  1. Orange11Sky
    02.10.2019 00:16

    Обязательно прослушаю ваши курсы.


  1. faoriu
    02.10.2019 09:30

    Для начинающих было бы лучше учить что-то вроде ортодоксального C++, а все эти свистелки типа move-семантики и constexpr — преждевременная оптимизация и только лишний повод выстрелить себе в ногу, поскольку имеют кучу тонкостей, про которые люди вроде Мейерса пишут толстые книжки.


    1. telhin
      02.10.2019 16:41
      +1

      constexpr — преждевременная оптимизация и только лишний повод выстрелить себе в ногу

      еще ни разу не удавалось отстрелить ногу на constexpr, чуть посидеть, по-убирать ошибки компиляции да, но ногу я еще ни разу не отстреливал.


      Со своей колокольни интереснее как начать пользоваться https://github.com/microsoft/GSL вроде вся мотивация прописана в https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md но программисты такие: "Да зачем, я умею работать с массивами / ресурсами / указателями / кастами типов", "Да тут глобальную переменную бросил, там extern и все работает" и так далее.


  1. excoder
    02.10.2019 12:39

    Низкий поклон за эту работу, ребята. Ощущение при просмотре, что акценты расставлены очень релевантно.


  1. Laryx
    02.10.2019 12:50

    Вопрос, а можно ли проанализировать с помощью PVS-Studio код MQL ?

    Сам язык — очень близок к С++, но среда разработки там своя (хотя, можно использовать и Visual Studio). Было бы интересно прогнать свои файлы под PVS, поглядеть, что там можно нарыть…


    1. SvyatoslavMC
      02.10.2019 15:47

      Всё зависит от компилятора. Часто за основу берут GCC или Clang, тогда мы проверяем код для специфичных платформ. MQL5 выглядит как не имеющий ничего общего с C++ в этом плане.


      1. Laryx
        02.10.2019 19:19

        Ничего общего??? Странно… Многие свои классы я перенёс из MSVC в MetaTrader без малейших изменений. Остальные требовали лишь изменения названий функций на специфические. Да и в кодах Стандартной Библиотеки MQL прослеживается явное влияние библиотеки MFC.

        Ну… Ладно…