C/C++ Ready | Программирование
15.8K subscribers
1.5K photos
94 videos
750 links
Авторский канал по разработке на C и C++.
Ресурсы, гайды, задачи, шпаргалки.
Информация ежедневно пополняется!

Cотрудничество: @energy_c

РКН: https://clck.ru/3QREHc
Download Telegram
Ищем пять самых частых событий на C++!

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

В этой задаче:
• считаем количество каждого события
• частично сортируем кандидатов
• выводим только первые пять


Такой подход удобен для небольшого отчёта по логам и метрикам. Для очень большого числа уникальных значений отдельно продумай ограничение памяти.

📣 C++ Ready | #задача
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
❤5🔥3👍2
Почему std::string_view может быть опасным?

std::string_view удобен, когда нужно передать строку без копирования. Он хранит только указатель и размер, поэтому работает быстро.

Но важный нюанс в том, что string_view не владеет памятью. Он просто смотрит на данные, которые должны жить где-то ещё.

Опасный код может выглядеть так:
std::string_view name = make_name();

std::cout << name;


Если make_name возвращает std::string, временная строка уничтожится сразу после выражения. string_view останется смотреть в память, которая уже не принадлежит строке.

Похожая проблема бывает при возврате view из функции:
std::string_view title() {
std::string value = "ready";
return value;
}


Здесь value живёт только внутри функции. После return ссылка на его данные становится висячей.

Безопаснее возвращать owning-строку:
std::string title() {
return "ready";
}


А string_view хорошо подходит для параметров функции, когда вызывающий код владеет строкой:
void log_name(std::string_view name) {
std::cout << name;
}


Так функция не копирует строку, но и не пытается продлить её жизнь.

string_view можно хранить недолго и принимать как параметр, но нельзя использовать как замену std::string для владения данными.

📣 C++ Ready | #совет
Please open Telegram to view this post
VIEW IN TELEGRAM
❤5🔥5👍4
Шпаргалка по основным операциям std::vector!

std::vector хранит элементы в непрерывной памяти и автоматически управляет их количеством. Карточка помогает быстро вспомнить не только названия методов, но и то, как меняется содержимое контейнера после вызова.

На картинке собраны создание и присваивание вектора, size и capacity, доступ к элементам, итераторы, вставка и удаление. Отдельно показаны reserve, push_back и emplace_back, а также разница между размером и выделенной памятью.

📣 C++ Ready | #ресурс
Please open Telegram to view this post
VIEW IN TELEGRAM
👍10❤4🔥3
Почему operator[] у map может изменить контейнер?

В C++ у std::map и std::unordered_map индексатор выглядит как обычное чтение значения по ключу.

Например, просто проверить счётчик:
std::map<std::string, int> hits;

std::cout << hits["guest"];


Код выведет 0, но важный момент в другом. Ключ guest будет добавлен в map, потому что operator[] создаёт значение по умолчанию, если ключа ещё нет.

После такого чтения контейнер уже не пустой:
std::cout << hits.size();


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

Если нужно только проверить наличие ключа, лучше использовать contains:
if (hits.contains("guest")) {
std::cout << "found";
}


Если нужно получить значение без вставки, подходит find:
auto it = hits.find("guest");

if (it != hits.end()) {
std::cout << it->second;
}


А operator[] оставляй для случаев, когда вставка действительно ожидается:
hits["guest"]++;


В такой строке поведение как раз удобно. Если ключа не было, счётчик создастся с нулём и сразу увеличится.

Для чтения используй find или contains, для создания и обновления можно использовать operator[].

📣 C++ Ready | #совет
Please open Telegram to view this post
VIEW IN TELEGRAM
❤7👍6🔥4
Схема раздельной компиляции проекта на C++!

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

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

Сохрани, чтобы не потерять!

📣 C++ Ready | #ресурс
Please open Telegram to view this post
VIEW IN TELEGRAM
❤12👍3🔥1
Выучить программирование — это только половина дела.

Нужно ещё найти работу 👨‍💻

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

Поэтому хочу поделиться инструментом, который может упростить эту задачу.

Это IT-проект на базе нейросети, который помогает искать вакансии и находить возможности для трудоустройства. Вместо того чтобы тратить всё свободное время на поиск, можно воспользоваться ИИ и сосредоточиться на подготовке к собеседованиям и развитии навыков.
Please open Telegram to view this post
VIEW IN TELEGRAM
👎1
Почему результат std::clamp не всегда стоит сохранять как ссылку?

std::clamp ограничивает число снизу и сверху. Это удобно для громкости, процентов, координат и других значений с допустимым диапазоном:
int volume = std::clamp(requested, 0, 100);


Но у функции есть деталь, которую легко пропустить. Она возвращает не новое значение, а константную ссылку на один из трёх аргументов.

Когда все аргументы живут достаточно долго, ссылка может быть полезна:
int value = 120;
int low = 0;
int high = 100;
const int& limited = std::clamp(value, low, high);


Здесь limited ссылается на high. Если изменить high, изменится и значение, видимое через limited.

А вот с временными границами хранить ссылку опасно:
int value = 120;
const int& limited = std::clamp(value, 0, 100);


В этом случае выбранная граница 100 существует только до конца выражения. После точки с запятой limited становится висячей ссылкой, и читать её нельзя.

Сохранить результат по значению:
int limited = std::clamp(value, 0, 100);


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

📣 C++ Ready | #совет
Please open Telegram to view this post
VIEW IN TELEGRAM
❤6👍4🔥4
Зачем сегодня выбирать C++ и где его возможности действительно нужны?

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

В статье разбирают:
• почему контроль над памятью даёт производительность и повышает цену ошибок
• как санитайзеры и профилировщики помогают искать проблемы до продакшена
• где C++ оправдан, а где проще выбрать другой язык и масштабировать систему

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

Продолжай читать на Habr!


📣 C++ Ready | #ресурс
Please open Telegram to view this post
VIEW IN TELEGRAM
❤8👍2🔥2
Почему std::move иногда всё равно копирует объект?

std::move сам ничего не переносит, а только приводит выражение к rvalue-ссылке, чтобы компилятор мог выбрать подходящий конструктор.

Посмотри на тип объекта до и после вызова:
const std::string source = "payload";
auto&& candidate = std::move(source);


candidate имеет тип const std::string&&. Константность не исчезает, потому что std::move сохраняет квалификаторы исходного объекта.

Обычный move-конструктор принимает неконстантную rvalue-ссылку:
std::string(std::string&& other);


Он может изменить исходную строку, забрав её внутренний буфер. С const std::string&& это невозможно, поэтому такой конструктор не подходит.

Для константного источника компилятор обычно выбирает копирующий вариант:
std::string::string(const std::string& other);


В результате код остаётся корректным, но большой буфер копируется. Сам факт, что написан std::move, не гарантирует перемещение.

Если владение можно передать, оставь источник неконстантным:
std::string source = "payload";
std::string result = std::move(source);


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

При возврате локального объекта std::move обычно тоже не нужен:
std::string make_message() {
    std::string result = "ready";
    return result;
}


Компилятор может применить NRVO и создать результат сразу в месте вызова. Принудительный std::move в return способен помешать этой оптимизации и не делает код быстрее автоматически.

📣 C++ Ready | #совет
Please open Telegram to view this post
VIEW IN TELEGRAM
❤6👍4🔥3
Шпаргалка по пределам числовых типов в C++!

std::numeric_limits<T> помогает узнать свойства типа прямо из кода. Для целых типов можно проверить границы, а для float и double наглядно сравнить точность и специальные значения.

На картинке показаны signed и unsigned типы, диапазоны float и double, а также lowest, min, max, epsilon и infinity.

Сохрани, чтобы не потерять!

📣 C++ Ready | #ресурс
Please open Telegram to view this post
VIEW IN TELEGRAM
❤9👍3🔥3