Информация
- В рейтинге
- 2 942-й
- Откуда
- Москва, Москва и Московская обл., Россия
- Дата рождения
- Зарегистрирован
- Активность
Специализация
Software Developer, Backend Developer
Junior
От 250 000 ₽
C++
Qt
Git
Node.js
JavaScript
CSS
HTML
Linux
Posix
GNU Make
Есть флаг компилятора
-fno-exceptions
который полностью убирает механизм исключений из C++Теоретически его можно использовать так, но это имеет смысл только в случае если код пишется под какой-либо одноплатник, где счёт идёт на байты, но даже так, более чем уверен, что накладных расходов на инстанцирование разных вариантов шаблонных методов будет больше, чем на использование таблицы виртуальных функций. Скоррее CRTP - это вариант более гибкого наследования, где механика реализации отдана на откуп программисту, за счёт чего она, с помощью "шаблонной магии", может попросту быть расширена.
Например, помимо указанного мной в комментарии выше варианта использования трейтса на проверку CRTP-наследника
is_crtp_base_of_v
, можно проверить, является ли наследник одного базового CRTP-класса наследником какого-либо другого базового CRTP-класса и в зависимости от этого включить/отключить какой-нибудь метод (черезrequires
) или же поменять логику существующего (черезif constexpr
). Иными словами "построить мост" в логике взаимодействия между двумя базовыми CRTP-классами одного CRTP-наследника.Так же можно создавать базовые классы-метки, без полей и методов, то есть они не будут производить никакого мутирующего воздействия на наследников, но базовые CRTP-классы смогут отловить сам факт, того, что их наследник наследуется от класса-метки через
std::is_base_of_v
и за счёт этого могут так же менять логику работы. Таким образом можно писать "настраиваемое наследование".Как видите идиома CRTP более гибкая нежели классический вариант наследования, условие одно - более или менее освоиться в работе шаблонов C++. Так же я считаю что данная идиома, отлично согласуется с наличием в C++ множественного наследования, попросту позволяя выжать из него больше.
CRTP так же довольно удобно использовать для реализации шаблонных интерфейсов взаимодействия между множеством классов-наследников от CRTP-интерфейса, не задействуя при этом виртуальные функции. Поскльку все наследники будут явно иметь методы CRTP-интерфейса, и при этом наилучшим вариантом будет применять ограничения шаблона через requires, чтобы шаблонные методы не инстанциировались на объекты иных типов.
Для ограничения шаблонов взаимодействия CRTP-наследников обычно использую в
requires
концептis_crtp_base_of_v
из объявления ниже:Допустим, нам нужно чтобы CRTP-наследники могли сравниваться между собой:
На выходе мы имеем результат, что все наследники CRTP-интерфейса могут взаимодействовать между друг-другом без каких-либо побочных эффектов и торчащих наружу "кишков" - полиморфизм на компилтайме.
По личному опыту: На ранних этапах изучения C++ делал шутан с видом сверху на Qt5 + QML. На C++ Qt создавал элементы через QQuickPaintedItem, а через QML размещал на "сцене". Проект торчал в топе таск менеджера по RAM из-за огромных утечек и люто лагал, поскольку главный цикл гонял все обработки событий + коллизию объектов в одном потоке. Понял я это только спустя время уже когда клепал другой пет-проект))
Совет: почитай про пул потоков (из теории можно почитать про системы массового обслуживания(СМО)), советую его использовать для обработки эвентов в игре ассинхронно. Так же чекай проект Valgrind'ом на утечки памяти. Подучи киллерфичу Qt - сигналы/слоты (не помню, но помоему они по дефолту юзают тред пул). Если хочешь сделать игру сетевой в Qt модуль QNetwork вполне неплохой.
А так же терпения и нервов тебе, плюсы неплохо умеют калечить. Будет казаться что ошибка где-то в компиляторе или библиотеке, но в процентах 80-ти она перед монитором.
Обязательно выучите базовые вещи в программировании: асимптотический анализ и структуры данных - данные темы сильно помогут в оптимизации кода Вашей программы. Плюсом будет хорошее понимание организация памяти процесса в операционных системах, на первых порах точно нужно знать: стэк, куча; В дальнейшем будет полезно (почитайте на досуге): виртуальная память, типы страниц(.text, .data, .bss, .noinit), маппинг страниц памяти / маппинг файлов в виртуальную память. Так же лучше выучить работу с процессами и методы межпроцессного взаимодействия (IPC). Выучите системы сборки: GNU Make, CMake. Что такое статические и динамические библиотеки и как их линковать в Ваш проект. Обязательно выучите средства отладки: GDB и Valgrind - они сильно облегчат жизнь
По многопоточности можно пробежаться в порядке усложнения темы. Доки по стандартной библиотеки C++ для многопоточности - https://en.cppreference.com/w/cpp/thread
Потоки: раздел Threads
Будет полезно понимание как процессор выполняет больше потоков, чем у него физических потоков, что такое переключение контекста и как оно влияет на производительность
Проблема состояния гонки (race condition) и методы её решения:
Критические секции: раздел Mutual exclusion
Условные переменные: раздел Condition variables
Атомики: раздел Atomic operations
Ассинхронные операции: раздел Futures
Так же будет полезно знать про корутины (удобно использовать в многопоточных программах): https://en.cppreference.com/w/cpp/language/coroutines
Обратите внимание на коллекцию библиотек C++ Boost - там есть много удобных вещей. Так же не плохо знать API операционных систем: WinAPI и POSIX API; хоть это и уровень Си, но применим в C++ (лучше изучать после Boost, чтобы не строить велосипедов).
Совет по набиванию руки: придумайте себе пет-проект(желатнльно посложней), не бойтесь строить велосипеды в учебных целях, чаще работайте с коммандной строкой.
Благодарствую за подсказку. Надо бы опробовать.
Предположим у нас есть Posix-совместимый код на C или C++ (такого кода очень много, сомневаться не стоит). И у нас появляется задача - сделать код полностью кроссплатформеным. Большая часть дела и так сделана, данный код пашет абсолютно где угодно - Linux, FreeBSD, Mac OS, Android и IOS ибо Posix-совместимо (по большей части). Но Windows по правилам не играет - Windows хочет быть долбанутым пациентом дурки. И из-за этого у нас есть два стула:
оборачивать весь код макросами для совместимости и писать под Windows используя наиболее близкое к Posix окружение(Cygwin/MinGW и т.п.);
либо оборачивать код теми же макросами, но во вдое большем колличестве, чтобы на выходе получался совершенно другой код для MSVC.
Казалось бы "первый стул удобней", но то тут то там возникают весьма интересные проблемы из разряда отсутствия поддержки именно UTF-8 в функциях работы с файлами начиная с обычных Сишного fopen, закначивая всем std::filesystem. Posix окружения нет, а его очень сильно не хватает поэтому и существет целый набор эти костылей к Windows которые раз за разом шлефуются. По итогу, если ты используешь что-то что плохо работает в MinGW или Cygwin то добро пожаловать в MSVC - пиши в два раза больше кода.
Мне лично, да и большенству больше чем уверен плевать, что там за тёрки у Microsoft с их CRL и прочую их закрытую муть, если эта муть работает нормально. Программистам нужна переносимость на уровне исходного кода которая была в C и C++ изначально и без лишнего пердолинга с макросами для совместимости. Врятли кому-то хочется тратить лишние человекочасы на перенос кода на одну "не такую как все" платформу.
Исчезнет возможность подхватывать this класса где создаётся лямбда через лямбда-захват:
Да, выглядит странно, но тем не менее это рабочий способ описать лаконично некоторые методы. Сам по себе деструктор не вычищает память из под экземпляра класса это делает только delete. По факту оператор delete - это комбинация вызовов деструкторов и последующий вызов free. Деструктор служит лишь для деинициализации экземпляра класса. Сделано это так чтобы можно было как переместить объект, так и скопировать:
Да, можно было бы заменить определение оператора присваивания на:
Но если разницы особой нет, особого смысла это не имеет. Иногда случается, что в классе множество разных поинтеров, память из которых нужно вычищать. Да, можно вынести код очистки в отдельный приватный метод например в
void clear()
и за тем уже вызывать их в деструкторах и операторах присвоения. Можно конечно ещё использовать std::unique_ptr.Что касательно использования placement-new - то он нужен лишь за тем чтобы вызвать конструктор у куска не инициализированной памяти, тоже вполне стандартная функция не взывающая никаких аллокаций/реаллокаций памяти кроме тех что прописаны в конструктор непосредственно.
Например им удобно пользоваться, если в некотором куске не инициализированной памяти надо сконструировать два разных экземпляра:
Из-за наличия инициализации в условных конструкциях:
Которая появилась в C++ 17-й версии, есть требования к версии C++ при компиляции кода, из-за этого и появилась версия C++ в названии, поскольку в некоторых версиях MinGW попрежнему явно приходится задавать версию при компиляции, если требуется поддержка версии C++ выше 14-й, я решил на это указать.
Хотелось бы уточнить, что именно не устраивает в участках кода, что вы отметили:
Вполне понятные и рабочие варианты сделать то что от данных методов требуется.
По поводу того, что это просто обёртка для работы с функциями OpenSSL - вы правы, стоило это указать в названии статьи, чтобы небыло заблуждений по поводу содержания. Уже исправил.
Вот пример аналога рекурсивной лямбда-функции без
std::function
:Да, это анонимная структура, но после компиляции останется лишь вызов перегрузки оператора, результат будет идентичен объявлению и вызову обычной лямбда-функции, но теперь у нас есть указатель
this
... но вот только теперь мы жертвуем лямбда-захватом, что в принципе сложно эмитировать при некоторых обстаятельствах. Кстати этот вариант лямбды можно засунуть вstd::function
, а следовательно и передовать в аргументы других функций, главное чтобы сигнатура перегрузки оператора совпадала с сигнатурой объявленной вstd::function
Вместо
starship
можно просто установитьfish
shell сoh-my-fish
и любой темой (никаких конфигов, всё просто и быстро):Debian based дистрибутивы(с пакетным менеджером apt):
Если нет в репозитории, повторить с предварительным выполнением:
Arch based дистрибутивы(с пакетным менеджером pacman):
Red Hat based дистрибутивы(с пакетным менеджером dnf):
с пакетным менеджером yum:
Установка как интерпретатор по умолчанию:
В графических терминалах ставится в основном через настройки профиля в поле "Команда" тебуется прописать
/bin/fish
илиusr/bin/fish
качаем исходнинки oh-my-fish и ставим его
Ставим тему, например chain (так как она работает в любых терминалах в том числе и termux на android'е)
Profit! Юзаем удобный терминал с автодополнением команд
Ссылки:
Fish: https://fishshell.com/
oh-my-fish: https://github.com/oh-my-fish/oh-my-fish
Список тем для oh-my-fish: https://github.com/oh-my-fish/oh-my-fish/blob/master/docs/Themes.md
Или просто установить
fish
:Debian based дистрибутивы(с пакетным менеджером apt):
Если нет в репозитории, повторить с предварительным выполнением:
Arch based дистрибутивы(с пакетным менеджером pacman):
Fedora based дистрибутивы(с пакетным менеджером dnf):
Установка как интерпретатор по умолчанию:
В графических терминалах ставится в основном через настройки профиля в поле "Команда" тебуется прописать
/bin/fish
илиusr/bin/fish
Без особых красот как
zsh
, но есть подсветка синтаксиса и автодополнение команд и всё это по default'у.Ссылка на официальный сайт с документацией и всем, всем, всем...
Небольшая поправочка:
Требуется запомнить размер буфера в
size
, иначе можно было и вовсе написать: