Senior C++ Developer
12.9K subscribers
1.29K photos
3 videos
581 links
№ 4931128893
Изучаем C++.

По вопросам сотрудничества: @adv_and_pr
Download Telegram
Различия между ссылками и указателями

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

Ссылки

- Ссылка - это псевдоним для уже существующей переменной и создается с момента определения.
- Нельзя создать ссылку без инициализации; она должна быть связана с объектом.
- После инициализации нельзя переназначить ссылку на другой объект.
- Ссылка не имеет собственного адреса в памяти - взятие адреса ссылки даст адрес объекта, который она представляет.
- Ссылка не может быть "пустой" и всегда ссылается на валидный объект.
- Ссылка разыменовывается автоматически при использовании, избегая необходимости явного разыменования.
- Ссылку нельзя переопределить, она всегда ссылается на один и тот же объект после инициализации.

Указатели

- Указатель - это переменная, которая хранит адрес объекта в памяти.
- Указатель может быть инициализирован, но может быть и просто объявлен, в отличие от ссылки, и, следовательно, может быть "пустым" (например, иметь значение nullptr).
- Указатель необходимо явно разыменовывать для доступа к значению объекта, на который он указывает.
- Указатели могут использоваться для создания множественных уровней индирекции (например, указатель на указатель).
- Можно объявлять массивы указателей, в отличие от ссылок.
- Указатели поддерживают арифметику указателей: их можно инкрементировать и декрементировать, что полезно при работе с массивами и итерировании.
- В случае указателей можно применять сравнения, в том числе с проверкой на nullptr, чтобы определить, указывает ли указатель на объект.

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

Критическая секция — это участок кода, который должен быть выполнен только одним потоком в определенный момент времени, чтобы избежать гонок данных (race conditions) и сохранить целостность данных. Для обеспечения безопасности в многопоточной среде можно использовать различные механизмы синхронизации, такие как мьютексы (mutexes) и блокировки (locks).

В примере на картинке два потока вызывают функцию someFunction, которая содержит критическую секцию, защищенную мьютексом mtx. Перед выполнением критической секции поток блокирует мьютекс с помощью mtx.lock(), а после выполнения разблокирует его с помощью mtx.unlock(). Это гарантирует, что только один поток может находиться внутри критической секции в определенный момент времени.

Помимо std::mutex, в C++ также существуют другие механизмы синхронизации, такие как std::lock_guard и std::unique_lock, которые упрощают работу с мьютексами и делают код более безопасным.

#для_продвинутых
<unistd.h>

<unistd.h> — это заголовочный файл в языке программирования C и C++, который предоставляет доступ к некоторым системным вызовам (system calls) в операционных системах Unix-подобных, таких как Linux.

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

Вот некоторые из наиболее часто используемых функций и символов <unistd.h>:

fork(): Создание нового процесса.
exec(): Замена текущего процесса новым процессом.
dup(): Дублирование файловых дескрипторов.
close(): Закрытие файловых дескрипторов.
read(): Чтение данных из файла или дескриптора.
write(): Запись данных в файл или дескриптор.
getpid(): Получение идентификатора текущего процесса.
getppid(): Получение идентификатора родительского процесса.

#для_продвинутых
Dependency Injection

Dependency Injection (DI) — это паттерн проектирования, который помогает управлять зависимостями в приложениях. Он особенно важен в объектно-ориентированных языках программирования, таких как C++, где классы и объекты играют центральную роль.

DI предполагает, что зависимости (например, объекты других классов, которые класс использует) должны передаваться в класс извне, а не создаваться им самостоятельно. Это делает класс более независимым и более тестируемым. В C++, DI можно реализовать следующими способами: внедрение через конструктор, внедрение через метод и использование фабрик. В картинке с примером мы используем внедрение через конструктор, так как это самый распространенный способ DI в C++. В конструкторе класса вы передаете зависимости как параметры.

Использование DI в C++ способствует лучшей организации кода, более простой поддержке и тестированию. Он позволяет избегать жестких зависимостей и делает ваш код более гибким и расширяемым.

#для_продвинутых
Spinlock

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

Spinlock работает следующим образом:

1. Поток, который хочет получить доступ к общему ресурсу, пытается захватить spinlock.
2. Если spinlock свободен, поток захватывает его и продолжает выполнение.
3. Если spinlock уже занят другим потоком, текущий поток не блокируется в ожидании ресурса, а активно «крутится» (spin) в цикле, проверяя, не освободился ли spinlock. Это называется «захватом на занятом ресурсе».
4. Как только spinlock становится доступным, поток захватывает его и продолжает выполнение.

#для_продвинутых
«Static initialization order fiasco»

«Static initialization order fiasco» (фиаско порядка статической инициализации) — это проблема, которая может возникнуть в C++ при инициализации статических переменных или объектов в разных переводимых единицах или при использовании статических переменных в разных библиотеках.

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

#для_продвинутых
std::unordered_map

std::unordered_map в C++ является частью стандартной библиотеки и представляет собой реализацию хеш-таблицы, которая позволяет хранить пары ключ-значение и обеспечивает быстрый доступ к значениям по ключу. Она является одним из контейнеров STL (Standard Template Library).

#для_продвинутых
Декларация

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

Обратите внимание, что декларации часто используются в заголовочных файлах (.h или .hpp), чтобы предоставить интерфейс к функциям или классам, а затем реализация определяется в соответствующих исходных файлах (.cpp). Это помогает разделить интерфейс и реализацию и упростить структуру проекта.

#для_начинающих
В чем отличие malloc от new?

malloc — выделение блока памяти в стиле Си, опасное с точки зрения приведения типов (non-typesafe), т.к. возвращает void* и требует обязательного приведения.

new — выделение блока памяти и последующий вызов конструктора, безопасное с точки зрения приведения типов (typesafe), т.к. тип возвращаемого значения определен заранее.

#для_продвинутых
override

Ключевое слово override используется для указания того, что функция в производном классе представляет собой переопределение (override) функции базового класса. Это помогает убедиться, что функция в производном классе действительно переопределяет функцию базового класса, и при компиляции будет сгенерирована ошибка, если это не так.

#для_начинающих
Флажки компиляции

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

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

#для_продвинутых
Stellarium — это бесплатное программное обеспечение GPL, которое визуализирует реалистичное небо в реальном времени с помощью OpenGL. Он доступен для Linux/Unix, Windows и macOS. В Stellarium вы действительно видите то, что можете увидеть своими глазами, биноклем или небольшим телескопом.

https://www.libhunt.com/r/stellarium
Redis plus plus

Redis – это быстрое хранилище данных типа «ключ‑значение» в памяти с открытым исходным кодом.

Redis plus plus - это клиентская библиотека C++ для работы с Redis.

git clone https://github.com/redis-developer/redis-plus-plus-modules.git
cd redis-plus-plus-modules
./bootstrap.sh
./configure
make -j8

🖥 Github
Please open Telegram to view this post
VIEW IN TELEGRAM
Что такое виртуальный деструктор и зачем он используется в C++?

В C++ виртуальный деструктор используется для правильного освобождения памяти при удалении объекта через указатель на базовый класс. Если базовый класс имеет виртуальный деструктор, то при удалении объекта через указатель на базовый класс будет вызван деструктор не только базового класса, но и всех его производных классов. Это позволяет избежать утечек памяти и неопределенного поведения при работе с полиморфными объектами.

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

Поиск в ширину (breadth-first search, BFS) - это алгоритм поиска или обхода графа. Он исследует все вершины на одном уровне, прежде чем переходить к следующему уровню.

Этот пример иллюстрирует обход в ширину для следующего графа:
0
/ \
1-----2
\
3
Начиная с вершины 2, алгоритм BFS посетит вершины в следующем порядке: 2, 0, 3, 1.
Статья Bungie о том, как они программируют такие игры, как Destiny, с использованием C++, и о правилах, которые они для этого создали.

https://www.bungie.net/7/en/News/Article/50666
std::invoke

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

Синтаксис:
std::invoke(callable, args...);


callable: объект, который будет вызван как функция. Это может быть указатель на функцию, функтор, лямбда-выражение или любой другой объект, который имеет оператор вызова().
args: аргументы, которые будут переданы callable.

#для_продвинутых
std::placeholders

std::placeholders — это пространство имен в стандартной библиотеке C++, которое содержит набор объектов-заполнителей (_1, _2, ..., _N), используемых при работе с функцией std::bind.

Функция std::bind позволяет связывать аргументы с функцией, создавая новый объект-функцию, который может быть вызван позже. Заполнители в std::placeholders используются для обозначения мест в списке аргументов, где будут подставлены значения при вызове нового объекта-функции.

#для_продвинутых