Грокаем C++
9.37K subscribers
49 photos
1 video
3 files
565 links
Два сеньора C++ - Владимир и Денис - отныне ваши гиды в этом дремучем мире плюсов.

По всем вопросам (+ реклама) @ninjatelegramm

Менеджер: @Spiral_Yuri
Реклама: https://telega.in/c/grokaemcpp
Мы на TGstat: https://tgstat.ru/channel/@grokaemcpp/stat
Download Telegram
Channel created
Hello World!
84👍19🤝8🔥4👏1
Приветственный пост

Рады приветствовать всех на нашем канале!
Вы устали от скучного, монотонного, обезличенного контента по плюсам?

Тогда мы идем к вам!

Здесь не будет бесполезных 30 IQ постов, сгенеренных ChatGPT, накрученных подписчиков и активности.

Канал ведут два сеньора, Денис и Владимир, которые искренне хотят делится своими знаниями по С++ и создать самое уютное коммьюнити позитивных прогеров в телеге!
(ну вы поняли, да? с++, плюс плюс, плюс типа
позитивный?.. ай ладно)

Жмакай и попадешь в наш чат. Там обсуждения не привязаны к постам, можете общаться на любые темы.

Материалы для новичка

ГАЙДЫ:

Мини-гайд по собеседованиям
Гайд по тестовым заданиям
Гайд по категория выражения и мув-семантике
Гайд по inline

Дальше пойдет список хэштегов, которыми вы можете пользоваться для более удобной навигации по каналу и для быстрого поиска группы постов по интересующей теме:
#algorithms
#datastructures
#cppcore
#stl
#goodoldc
#cpp11
#cpp14
#cpp17
#cpp20
#commercial
#net
#database
#hardcore
#memory
#goodpractice
#howitworks
#NONSTANDARD
#interview
#digest
#OS
#tools
#optimization
#performance
#fun
#compiler
#design
#exception
#guide
#задачки
#base
#quiz
#concurrency
#ЧЗХ
#ревью
🔥3719👍15🤔2🐳1
База алгоритмов STL

Когда я только изучал плюсы, меня приводило в ступор обилие и разнообразие в стандартной библиотеке. Контейнеры еще куда ни шло. Их довольно немного, а, при знании их устройства, с ними довольно комфортно работать per se.
Но вот алгоритмы…

Их дохрена, хрен их запомнишь и хрен поймешь еще, когда их использовать.

А что, если я скажу, что в бэкэнд разработке в 99% случаев будут использоваться только три алгоритма?
Естественно, в этот список не входят пустышки, типа
std::swap и прочей тривиальщины.

В следующих постах буду раскрывать каждый из этих
алгоритмов.

Stay Cool.

#STL #algorithms
👍28🤡63🔥3
В продолжение к предыдущему посту

Хочу пояснить пометку «в бэкэнд разработке».
C++ - очень мощная инструмент. Так или иначе большинство современных языков написаны с его использованием (первичный компилятор, интерпретатор), почти все системное ПО написано на связке С/С++, браузеры, нейросети, дата сайенс инструменты.

Плюсы везде

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

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

Есть такой мем, что люди учатся в Бауманке, потом жестко ботают языки и алгоритмы, чтобы наконец устроиться в компанию мечты - Яндекс. А потом всю карьеру перекладывают джейсоны да эксемельки из одного места в другое.
Ну да. А что вы хотели от бэкэнда? Если бизнесу нужно перекладывать джейсоны, ты будешь их перекладывать. И это нормально. Далеко не все проекты rocket science и это нужно принять.

Так вот. Если вам не подходят методы стандартных контейнейров, то скорее всего вам нужна какая-то кастомная нетривиальная обработка. С которой алгоритмы STL, при всей их обобщенности, не справляются.

Stay cool.
😁33👍6❤‍🔥2🤣2🔥1🤡1
Ссылки vs Указатели

Давайте раз и навсегда разберем, чем отличаются эти две сущности.

Новички всегда встают в ступор при вопросе: "Чем отличаются ссылки и указатели?". Помню когда-то и я поплыл на том же вопросе при устройстве на стажировку в Меру. А все потому что вопрос насколько базовый, настолько же и нерелевантный к реальному программированию на с++. Смелое и спорное утверждение. Но оставайтесь на связи, все будет в следующих постах. А сейчас суть.

Ссылка

📎 Является псевдонимом переменной
📎 Нельзя декларировать, только определять. Ссылка не может быть неинициализированной
📎 Один раз инициализируется, переприсвоить ее нельзя
📎 Объект инициализации должен иметь адрес
📎 Не имеет своего адреса. Попытка взять адрес от ссылки вернёт адрес объекта, на который ссылка указывает
📎 Не может иметь невалидное значение, так как инициализируются существующим обьектом на стеке
📎 Разыменовывается автоматически при использовании
📎 Не бывает константной. В этом нет смысла, так как ссылка и так неизменяема. То есть нельзя перенаправить ссылку на другой объект.
📎 Является обёртка над указателем с лимитированы функционалом (над константным указателем строго говоря, из этого и следует предыдущий пункт)

Указатель

👉🏼 Является переменной на стеке, содержащей типизированный адрес куска памяти
👉🏼 Для получения значения по указателю его нужно разыменовать, что довольно неприятно и неудобно
👉🏼 Может иметь сколько угодно уровней индирекции. То есть я могу определить указатель на указатель на указатель на инт. Ссылки никого не могут
👉🏼 Можно объявить массив указателей. Объявить массив ссылок нельзя
👉🏼 Самое практичное отличие - у указателей есть своя арифметика. Для них определены + и -. Ссылки в такое не могут
👉🏼 Имеет определённое невалидное значение nullptr

Скорее всего все это вам не пригодится, а то, что пригодится, освоится интуитивно. Но теперь вы везде можете козырять этим знанием.

Stay cool.

#cppcore
50👍17🔥9😁1
Сортировка

std::sort - самый нужный и наиболее часто используемый алгоритм из библиотеки STL. Не зря мир серьезных алгоритмов маленькие хакеры начинают познавать с сортировки. Конечно после изучения всех базовых концепций языка и программных сущностей. Это реально нужная на практике вещь.

Давайте на пальцах.

Есть у вас вектор для каких-то объектов. Вы заполняете этот вектор объектами просто с помощью push_back. Здесь есть 2 варианта.

Первый вариант - порядок элементов неважен, работаем со всеми элементами по очереди или только с крайними(ну например очередь задач или список слушателей евентов класса). Тут все просто, испотльзуй методы контейнера и живи счастливо.

Второй вариант - вы хотите переупорядочить элементы в контейнере или сделать вставку/удаление/поиск/изменение элемента из середины контейнера. Тут все сложнее. Для начала надо подумать, потому что вы скорее всего выбрали неправильный контейнер. Алгоритмы find, remove и тому подобное - от Иблиса. Может показаться, что линейная сложность не проблема, не так уж это и много. Но нет. Вряд ли вам нужно один раз найти какой-то элемент к контейнере и скорее всего там будет квадратичная сложность. Про удаление я вообще молчу, внутреннее устройство вектора даст вашему первомансу отдохнуть.
Присмотритесь к ассоциативным контейнерам, когда вам нужны такие операции. И если нужно переупорядочивать элементы, то тоже проверьте себя еще раз.
Проверили? Идем дальше. У нас остается вектор и вы хотите его переупорядочить. Переупорядочить - назначить отношение порядка. Значит можно сказать, какой элемент идет за каким. Значит можно сказать, какой элемент "меньше" другого. Так и приходим к сортировке.

По факту, сортировка - единственный алгоритм, который реально можно применить для последовательных контейнеров. Ну ладно не единственный. Но это уже тема для другого поста.

Stay cool.

#STL #algorithms
👍287🔥2
Small string optimization

Люди, которые разрабатывают компилляторы - гении. Никто не знает тех маленьких индусов, которые рвут свои маленькие попки, чтобы из твоего зачастую дурнопахнущего кода на С++ сделать конфетку. Эта фича не является геймченджером или сильным бустром перфоманса. Это просто крутая оптимизация, которая исходит из понимания работы с памятью на низком уровне и юз-кейсов класса строки.

Дело в том, что в основе плюсовой строки лежит обычный сишный массив. Точнее не обычный, а выделенный в куче. Так вот, когда вы пишите std::string var = "Like this post", по идее на куче должен выделяться блок памяти, равный размеру этой строки. Но. Зачастую пользователь класса std::string хочет присвоить переменной какую-нибудь короткую строку, которую он менять не собирается. Зачем тогда тратить драгоценные клоки проца и делать дорогостоящий вызов, когда можно поместить эту короткую строку в маленький внутренний буффер самого объекта строки и жить счастливо? Не дергать аллокатор, не фрагментировать память. Хорошо звучит? Вот и индусы так же подумали и запилили small string optimization.

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

Давайте все быть такими же умными, как индусы.

Stay cool.

#optimization #datastructures
👍31🔥92👎1
struct vs class

Если ты перед каждым объявлением класса думаешь над тем, какое ключевое слово использовать - ты по адресу.

Я лично всегда триггерился, когда полноценный класс объявляют структурой. Да где это виданно?!? Ты объявляешь класс, в языке есть очень похожее ключевое слово, которое говорит, что началось объявление класса. И ты, как полностью психически здоровый человек, пишешь struct. Круто че. Мои полномочия тут все.

И вот ты хочешь сделать "по правильному" и объявить класс классом. А потом понимаешь, что половина проекта использует struct, да еще в коде, для которого твой класс предназначен, тоже используются структуры. И закрадываются сомнения. Может это не мир неправильный, а я?

Но потом пришло принятие и понимание. Структуры проще писать. Может я один такой, конечно, невнимательный. Но насколько часто вы встречаете ошибку с использованием приватного метода вне класса? У меня это ошибка постоянно всплывает, когда я использую class. Меня это ужасно раздражало, поэтому сдался и начался просто всегда писать struct.

Все потому, что struct обладает двумя преумуществами в реальной разработке по сравнению с class. Все поля публичные и наследование тоже публичное.
Вспомните, сколько раз вам было нужно приватное наследование? Если хоть раз и оправданно, то вы бриллиант. А челяди эта фича не нужна. То есть уже на одно слово меньше нужно писать.
А исходя из концепции ООП, нам обычно нужно скрыть данные от чужих глаз. Я не думаю над тем, как открыть мой публичный интерфейс. Я думаю над тем, что мне нужно скрыть. Поэтому очень органично после публичных методов я пишу private: и дальше пишу всю запрещенку. Да и принято так, что публичный интерфейс описывается всегда первым. Поэтому я могу не писать лишний раз public:.

Экономится целых 1 слово на класс и еще одно на наследуемый класс. Это ли не причина перейти на struct?)

Stay cool.

#cppcore
🤷‍♀23😁15👎95👍3🔥3
CTAD - Class Template Argument Deduction

Одним из приятных нововведений C++ 17 стандарта стало автоматическое выведение типа классов при объявлении шаблонных объектов, функций и т.д.

Этот механизм позволяет вам явно не указывать тип аргументов шаблонных классов там, где они могут быть выведены компилятором. Пример:
// vector declaration before C++17
std::vector<double> v1 = {1.0, 2.0, 3.0};

// CTAD vector declaration since C++17
std::vector v2 = {1.0, 2.0, 3.0};


Давайте посмотрим на живой пример. Обратите внимание, что в строке параметров компилятора указан 17 стандарт -std=c++17. Если его попробовать переключить на более ранний стандарт -std=c++14, то возможность компиляции будет утрачена.

Ключевым моментом CTAD для компилятора является возможность вывести этот тип. Если при попытке выведения появляется несколько кандидатов, то попытка выведения типа приведет к ошибке. Пример:
// Error: no viable constructor or deduction guide for deduction of template arguments of 'vector'
std::vector data = {1.0, 2.0, 3, 4};

В данном случае тип передаваемых значений различен: double и int. Компилятор не будет брать на себя ответственность выбирать какой-то тип самостоятельно. Вдруг это принципиально важно для корректности исполнения ваших программ?

Однако, так же стоит и помнить про неожиданные эффекты CTAD. При инициализации из одного аргумента типа, который является специализацией рассматриваемого шаблона класса, вывод копирования обычно предпочтительнее переноса по умолчанию:
// The type is std::vector<int>
std::vector v1{1, 2};

// The type is std::vector<int>
std::vector v2{v1};

// The type is std::vector<std::vector<int>>
std::vector v3{v1, v2};


Компилятор дает большее предпочтение конструкторам initializer-list, но при list-initialization остается неизменным:
// The type is std::vector<int>
std::vector v1{1, 2};

// The type is std::vector<int>
std::vector v2(v1.begin(), v1.end());

// The type is std::vector<std::vector<int>::iterator>
std::vector v3{v1.begin(), v1.end()};


Подробнее: cppreference

#cpp17
👍154🔥2
# Комментарии в коде

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

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

Давайте длинные и информативные имена переменным и методам. Это настолько приятно читать и даже жить как-то проще становится.
Разбивайте код на элементарные части. Чтобы каждый вызов метода был информативным и точно отражал свою функцию. И чем больше элементарных структурных элементов в коде (элементарных не значит минимального размера), тем лучше и более читаемым он становится.

Пишите комменты только для действительно нетривиальных вещей. Когда у потенциального читателя может возникнуть вопрос: "а как получается, что эта последовательность действий приводит к этому результату?" - вот то место, куда нужно вставить коммент.

Stay cool.

#goodpractice
👍23👎4❤‍🔥22🔥1🌚1