C и C++ имеют свои собственные встроенные генераторы случайных чисел. Они реализованы в двух отдельных функциях, которые находятся в заголовочном файле
cstdlib
:srand()
устанавливает передаваемое пользователем значение в качестве стартового. srand()
следует вызывать только один раз — в начале программы (обычно в верхней части функции main()
)rand()
генерирует следующее случайное число в последовательности. Оно будет находиться в диапазоне от 0
до RAND_MAX
(константа в cstdlib
, значением которой является 32767
).Вот пример программы, в которой используются обе эти функции:
#include <iostream>
#include <cstdlib> // для функций rand() и srand()
int main()
{
srand(4541); // устанавливаем стартовое значение - 4 541
// Выводим 100 случайных чисел
for (int count=0; count < 100; ++count)
{
std::cout << rand() << "\t";
// Если вывели 5 чисел, то вставляем символ новой строки
if ((count+1) % 5 == 0)
std::cout << "\n";
}
}
// 14867 24680 8872 25432 21865
// ...
В C++ 11 добавили тонну нового функционала для генерации случайных чисел, включая алгоритм Вихрь Мерсенна, а также разные виды генераторов случайных чисел (например, равномерные, генератор Poisson и пр.). Доступ к ним осуществляется через подключение заголовочного файла
random
. Вот пример генерации случайных чисел в C++11 с использованием Вихря Мерсенна на картинке.
Вихрь Мерсенна генерирует случайные 32-битные целые числа
unsigned
(а не 15-битные целые числа, как в случае с rand()
), что позволяет использовать гораздо больший диапазон значений. Существует также версия (
std::mt19937_64
) для генерации 64-битных целых чисел unsigned
@cpluspluc
Please open Telegram to view this post
VIEW IN TELEGRAM
👍17❤3⚡1🔥1💘1
C и C++ имеют свои собственные встроенные генераторы случайных чисел. Они реализованы в двух отдельных функциях, которые находятся в заголовочном файле
cstdlib
:srand()
устанавливает передаваемое пользователем значение в качестве стартового. srand()
следует вызывать только один раз — в начале программы (обычно в верхней части функции main()
)rand()
генерирует следующее случайное число в последовательности. Оно будет находиться в диапазоне от 0
до RAND_MAX
(константа в cstdlib
, значением которой является 32767
).Вот пример программы, в которой используются обе эти функции:
#include <iostream>
#include <cstdlib> // для функций rand() и srand()
int main()
{
srand(4541); // устанавливаем стартовое значение - 4 541
// Выводим 100 случайных чисел
for (int count=0; count < 100; ++count)
{
std::cout << rand() << "\t";
// Если вывели 5 чисел, то вставляем символ новой строки
if ((count+1) % 5 == 0)
std::cout << "\n";
}
}
// 14867 24680 8872 25432 21865
// ...
В C++ 11 добавили тонну нового функционала для генерации случайных чисел, включая алгоритм Вихрь Мерсенна, а также разные виды генераторов случайных чисел (например, равномерные, генератор Poisson и пр.). Доступ к ним осуществляется через подключение заголовочного файла
random
. Вот пример генерации случайных чисел в C++11 с использованием Вихря Мерсенна на картинке.
Вихрь Мерсенна генерирует случайные 32-битные целые числа
unsigned
(а не 15-битные целые числа, как в случае с rand()
), что позволяет использовать гораздо больший диапазон значений. Существует также версия (
std::mt19937_64
) для генерации 64-битных целых чисел unsigned
@cpluspluc
Please open Telegram to view this post
VIEW IN TELEGRAM
👍9❤3⚡1🔥1
#include "include/cef_app.h"
int main(int argc, char* argv[]) {
CefMainArgs main_args(argc, argv);
CefSettings settings;
// выполняем инициализацию
CefInitialize(main_args, settings, nullptr, nullptr);
// создаем окно с UI
CefWindowInfo window_info;
CefBrowserHost::CreateBrowser(window_info, new SimpleHandler(), "file:///your_ui.html", CefBrowserSettings(), nullptr);
// цикл обработки сообщений
CefRunMessageLoop();
// Cef для окончания работы
CefShutdown();
}
Замените
"file:///your_ui.html"
на путь к вашему HTML-файлу. Вуаля! Ваш интерфейс на HTML/CSS уже встроен в приложение на C++.@cpluspluc
Please open Telegram to view this post
VIEW IN TELEGRAM
❤9👍6🔥2⚡1
=default
(для конструктора по умолчанию)X::X() {}
// и
X::X() = default;
#include <iostream>
#include <type_traits>
struct X {
//X() {}
X() = default;
int a;
int b;
};
int main( ) {
X x { 1, 2 }; // ошибка, если X - не тривиальный класс.
std::cout << std::boolalpha <<
std::is_trivial<X>::value << "\n";
}
При определении конструктора как
= default
тривиальность класса сохраняется, если она была до этого. В данном случае, это равносильно отсутствию явного упоминания конструктора в определении класса.Если конструктор по умолчанию определен как
= default
вне определения класса, всё равно будет считаться, что конструктор предоставлен пользователем, и это тоже делает класс нетривиальным.@cpluspluc
Please open Telegram to view this post
VIEW IN TELEGRAM
👌8👍7❤5⚡1🔥1
C++ Requests — это простая обертка вокруг libcurl, вдохновленная Python Requests.
Вот так можно отправить GET-запрос при помощи cpr:
#include <cpr/cpr.h>
int main(int argc, char** argv) {
cpr::Response r = cpr::Get(cpr::Url{"https://api.github.com/repos/libcpr/cpr/contributors"},
cpr::Authentication{"user", "pass", cpr::AuthMode::BASIC},
cpr::Parameters{{"anon", "true"}, {"key", "value"}});
r.status_code; // 200
r.header["content-type"]; // application/json; charset=utf-8
r.text; // JSON text string
}
@cpluspluc
Please open Telegram to view this post
VIEW IN TELEGRAM
👍26❤2
🔓Что выведет следующий код?
🔢Варианты ответа:
A)
B)
C)
D)
✅Правильный ответ: B
💡Почему?
В момент вызова конструктора Base, объект ещё не стал Derived. Виртуальная функция вызывается в контексте Base.
#include <iostream>
using namespace std;
class Base {
public:
Base() {
cout << "Base constructor: ";
call();
}
virtual void call() { cout << "Base::call\n"; }
};
class Derived : public Base {
int x = init();
int init() {
cout << "Derived::init\n";
return 42;
}
public:
Derived() {
cout << "Derived constructor\n";
}
void call() override {
cout << "Derived::call, x = " << x << "\n";
}
};
int main() {
Derived d;
return 0;
}
🔢Варианты ответа:
A)
Derived::init
Derived constructor
B)
Derived::init
Derived constructor
C)
Base constructor: Derived::call, x = 42
Derived constructor
D)
Derived::call, x = <undefined>
Derived::init
Derived constructor
✅
💡Почему?
В момент вызова конструктора Base, объект ещё не стал Derived. Виртуальная функция вызывается в контексте Base.
👍22🔥4🥰1
#include <iostream>
#include <vector>
struct Tracer {
Tracer(const char* name) : name(name) {
std::cout << "Constructing " << name << "\n";
}
~Tracer() {
std::cout << "Destructing " << name << "\n";
}
const char* name;
};
struct Example {
Tracer t1{"t1"};
std::vector<Tracer> list;
Tracer t2{"t2"};
Example() : list{Tracer("temp1"), Tracer("temp2")} {
std::cout << "Inside constructor\n";
}
};
int main() {
Example e;
std::cout << "End of main\n";
return 0;
}
---
ОТВЕТ:
Constructing t1
Constructing temp1
Constructing temp2
Inside constructor
Constructing t2
End of main
Destructing t2
Destructing temp2
Destructing temp1
Destructing t1
---
Почему так:
• Поля инициализируются в порядке объявления в структуре, а не в списке инициализации
• std::initializer_list создаёт временные объекты, которые копируются в вектор
• t2 создаётся после строки Inside constructor
• Деструкторы вызываются в обратном порядке: t2 → temp2 → temp1 → t1
Хитрость — в порядке инициализации, временных объектах и destructuring-порядке!
@cpluspluc
Please open Telegram to view this post
VIEW IN TELEGRAM
👍20❤4🔥2🎄1
Реализуйте структуру данных — очередь (`queue`), поддерживающую три операции:
-
enqueue(x)
— добавить элемент в конец очереди (должно работать за O(1));-
dequeue()
— удалить элемент из начала очереди и вернуть его (должно работать за O(1));-
remove(x)
— удалить первое вхождение элемента x
из очереди за O(1).Ограничения:
- Элементы могут повторяться.
- Если элемент отсутствует,
remove(x)
не делает ничего.- Операции должны оставаться эффективными на больших объемах данных (миллионы элементов).
- Можно использовать стандартные структуры данных STL (`list`,
unordered_map
, unordered_set
и т.д.).▪️ Дополнительные вопросы к кандидату:
- Как обеспечить O(1) удаление произвольного элемента из очереди?
- Как избежать утечек памяти или "битых" итераторов после удаления элементов?
- Что будет, если очередь будет содержать множество одинаковых элементов?
- Как бы вы изменили решение, если бы нужно было удалять все вхождения элемента, а не только первое?
---
▪️ Разбор возможного решения:
Основная архитектура:
- Используем
std::list<int> data
для хранения реальной очереди.- Почему
list
? Потому что позволяет за O(1) удалять элементы по итератору.- Используем
std::unordered_map<int, std::list<std::list<int>::iterator>> index
.- Для каждого значения храним список итераторов на его вхождения в
data
.Операции:
- enqueue(x):
- Добавляем
x
в конец data
.- Сохраняем итератор на него в
index[x]
.- Все действия — за O(1).
- dequeue():
- Удаляем элемент из начала
data
.- Находим соответствующий итератор в
index
, удаляем его.- Если в списке итераторов для этого значения больше нет элементов, удаляем ключ из
index
.- remove(x):
- Если в
index[x]
есть итераторы:- Берем первый итератор.
- Удаляем его из
data
и из списка итераторов.- Если список стал пустым — удаляем запись
x
из index
.- Все действия — за O(1).
---
▪️ Возможные подводные камни:
- ❗ Не проверять, пустой ли
index[x]
, перед удалением итератора.- ❗ Не удалять ключи из
unordered_map
, что приводит к накоплению "пустых" списков в памяти.- ❗ Использовать
vector
вместо list
— тогда удаление из середины будет занимать O(n).---
▪️ Мини-пример интерфейса класса:
#include <list>
#include <unordered_map>
class FastQueue {
private:
std::list<int> data;
std::unordered_map<int, std::list<std::list<int>::iterator>> index;
public:
void enqueue(int x) {
auto it = data.insert(data.end(), x);
index[x].push_back(it);
}
int dequeue() {
if (data.empty()) throw std::out_of_range("Queue is empty");
int val = data.front();
auto it = index[val].front();
index[val].pop_front();
if (index[val].empty()) {
index.erase(val);
}
data.pop_front();
return val;
}
void remove(int x) {
if (index.count(x) == 0) return;
auto it = index[x].front();
data.erase(it);
index[x].pop_front();
if (index[x].empty()) {
index.erase(x);
}
}
};
▪️ Дополнительные вопросы на усложнение:
- Что если нужно сделать
removeAll(x)
— удаление всех вхождений элемента?- Как изменится решение, если в очереди будут сложные объекты вместо
int
?- Как минимизировать использование памяти, если очередь очень большая?
- Как обеспечить безопасность потоков (thread-safety) для многопоточного варианта очереди?
@cpluspluc
Please open Telegram to view this post
VIEW IN TELEGRAM
👍19❤5🔥3
🎯 ▪ Задача: "Числа-близнецы с кастомным компаратором" (C++ для продвинутых)
У вас есть массив
1.
2.
3. Если таких пар несколько, вернуть все, отсортированные по A, затем по B
▪ Ограничения:
-
-
▪ Пример:
Вход:
Вывод:
✅ Минимальная разница = 1
🧠 Уловки задачи:
Наивное сравнение O(N²) слишком медленно
Нужно использовать сортировку + два указателя или бинарный поиск
✍️ Решение:
Идея решения — сначала разделить все числа на четные и нечетные, отсортировать их, а затем с помощью двух указателей найти пары с минимальной разницей. После этого собрать все такие пары и отсортировать результат по условию.
▪ Код:
```cpp
#include <iostream>
#include <vector>
#include <algorithm>
#include <cmath>
using namespace std;
int main() {
int N;
cin >> N;
vector<int> even, odd;
for (int i = 0; i < N; ++i) {
int x;
cin >> x;
if (x % 2 == 0) even.push_back(x);
else odd.push_back(x);
}
sort(even.begin(), even.end());
sort(odd.begin(), odd.end());
vector<pair<int, int>> result;
int min_diff = INT_MAX;
int i = 0, j = 0;
while (i < even.size() && j < odd.size()) {
int a = even[i];
int b = odd[j];
int diff = abs(a - b);
if (diff < min_diff) {
min_diff = diff;
result.clear();
result.emplace_back(a, b);
} else if (diff == min_diff) {
result.emplace_back(a, b);
}
if (a < b) i++;
else j++;
}
sort(result.begin(), result.end());
for (auto& p : result) {
cout << "(" << p.first << "," << p.second << ")\n";
}
return 0;
}```
▪ 🔍 Пошаговый разбор:
✅ Считываем входные данные
✅ Разделяем числа на четные и нечетные
✅ Сортируем оба массива
✅ Используем два указателя, чтобы найти минимальную разницу за O(N)
✅ Если нашли пару с меньшей разницей — сбрасываем результат и добавляем её
✅ Если нашли пару с такой же разницей — просто добавляем
✅ Финально сортируем все найденные пары по A, затем по B
✨ Почему задача хитрая: ✅ Нужно оптимальное решение вместо лобового перебора
✅ Включает тонкую работу с индексами
✅ Требует правильно обрабатывать несколько минимальных пар
✅ Объединяет знания сортировки, двух указателей и поиска пар
💪 Challenge для профи: 👉 Попробуйте реализовать эту задачу с помощью multiset + lower_bound / upper_bound, чтобы избежать сортировки массивов и упростить логику поиска ближайших чисел.
У вас есть массив
N
целых чисел. Нужно найти все пары чисел (A, B), таких что:1.
|A - B|
минимально среди всех пар в массиве2.
A
— четное, а B
— нечетное3. Если таких пар несколько, вернуть все, отсортированные по A, затем по B
▪ Ограничения:
-
1 <= N <= 10^5
-
-10^9 <= A[i] <= 10^9
▪ Пример:
Вход:
arr = [4, 7, 9, 2, 8, 3]
Вывод:
(2,3)
(4,3)
(4,7)
(8,7)
(8,9)
✅ Минимальная разница = 1
🧠 Уловки задачи:
Наивное сравнение O(N²) слишком медленно
Нужно использовать сортировку + два указателя или бинарный поиск
✍️ Решение:
Идея решения — сначала разделить все числа на четные и нечетные, отсортировать их, а затем с помощью двух указателей найти пары с минимальной разницей. После этого собрать все такие пары и отсортировать результат по условию.
▪ Код:
```cpp
using namespace std;
int main() {
int N;
cin >> N;
vector<int> even, odd;
for (int i = 0; i < N; ++i) {
int x;
cin >> x;
if (x % 2 == 0) even.push_back(x);
else odd.push_back(x);
}
sort(even.begin(), even.end());
sort(odd.begin(), odd.end());
vector<pair<int, int>> result;
int min_diff = INT_MAX;
int i = 0, j = 0;
while (i < even.size() && j < odd.size()) {
int a = even[i];
int b = odd[j];
int diff = abs(a - b);
if (diff < min_diff) {
min_diff = diff;
result.clear();
result.emplace_back(a, b);
} else if (diff == min_diff) {
result.emplace_back(a, b);
}
if (a < b) i++;
else j++;
}
sort(result.begin(), result.end());
for (auto& p : result) {
cout << "(" << p.first << "," << p.second << ")\n";
}
return 0;
}```
▪ 🔍 Пошаговый разбор:
✅ Считываем входные данные
✅ Разделяем числа на четные и нечетные
✅ Сортируем оба массива
✅ Используем два указателя, чтобы найти минимальную разницу за O(N)
✅ Если нашли пару с меньшей разницей — сбрасываем результат и добавляем её
✅ Если нашли пару с такой же разницей — просто добавляем
✅ Финально сортируем все найденные пары по A, затем по B
✨ Почему задача хитрая: ✅ Нужно оптимальное решение вместо лобового перебора
✅ Включает тонкую работу с индексами
✅ Требует правильно обрабатывать несколько минимальных пар
✅ Объединяет знания сортировки, двух указателей и поиска пар
💪 Challenge для профи: 👉 Попробуйте реализовать эту задачу с помощью multiset + lower_bound / upper_bound, чтобы избежать сортировки массивов и упростить логику поиска ближайших чисел.
👍9👀7❤3🔥3🌭1
🦾 Задача с подвохом: Виртуальные функции и конструкторы
Условие:
Что выведет следующий код и почему?
❓ Вопрос:
Что будет выведено? Почему результат может удивить даже опытных C++ разработчиков?
🔍 Разбор:
1️⃣ Мы создаём объект.
Это вызывает конструктор , но сначала выполняется конструктор `Base` (по правилам иерархии).
2️⃣ В конструкторе есть вызов .
Может показаться, что поскольку объект на самом деле , вызовется .
Но! Вот главный подвох:
➡️ В C++, когда вы вызываете виртуальную функцию из конструктора (или деструктора), она не виртуальна для объекта, который ещё не полностью сконструирован.
На момент вызова объект всё ещё только Base, потому что ещё не инициализирован.
✅ Пошаговое выполнение:
- Вызов конструктора :
```
Base constructor
```
- Вызов внутри конструктора :
Это вызовет Base::foo(), а не :
```
Base foo
```
- После завершения конструктора , вызывается конструктор :
```
Derived constructor
```
✅ Итоговый вывод:
```
Base constructor
Base foo
Derived constructor
```
💥 Подвох:
• Многие ожидают, что виртуальные функции работают «магически» всегда.
• Но при вызове из конструктора (или деструктора) виртуальные функции не полиморфны, потому что объект ещё не «стал» Derived полностью.
🛡️ Что нужно помнить:
Никогда не полагайтесь на вызовы виртуальных функций в конструкторах/деструкторах для вызова методов производных классов. Это источник трудноуловимых багов.
✅ Вывод:
C++ строго следует правилам объектной модели: пока объект конструируется (или разрушается), он считается экземпляром того класса, конструктор которого выполняется в данный момент. Это поведение важно помнить при проектировании иерархий классов!
@cpluspluc
Условие:
Что выведет следующий код и почему?
#include <iostream>
class Base {
public:
Base() {
std::cout << "Base constructor\n";
foo();
}
virtual void foo() {
std::cout << "Base foo\n";
}
};
class Derived : public Base {
public:
Derived() {
std::cout << "Derived constructor\n";
}
void foo() override {
std::cout << "Derived foo\n";
}
};
int main() {
Derived d;
return 0;
}
❓ Вопрос:
Что будет выведено? Почему результат может удивить даже опытных C++ разработчиков?
🔍 Разбор:
1️⃣ Мы создаём объект
Derived d;
Это вызывает конструктор
Derived
2️⃣ В конструкторе
Base
foo();
Может показаться, что поскольку объект на самом деле
Derived
Derived::foo()
Но! Вот главный подвох:
➡️ В C++, когда вы вызываете виртуальную функцию из конструктора (или деструктора), она не виртуальна для объекта, который ещё не полностью сконструирован.
На момент вызова
foo()
Derived
✅ Пошаговое выполнение:
- Вызов конструктора
Base
```
Base constructor
```
- Вызов
foo()
Base
Это вызовет Base::foo(), а не
Derived::foo()
```
Base foo
```
- После завершения конструктора
Base
Derived
```
Derived constructor
```
✅ Итоговый вывод:
```
Base constructor
Base foo
Derived constructor
```
💥 Подвох:
• Многие ожидают, что виртуальные функции работают «магически» всегда.
• Но при вызове из конструктора (или деструктора) виртуальные функции не полиморфны, потому что объект ещё не «стал» Derived полностью.
🛡️ Что нужно помнить:
Никогда не полагайтесь на вызовы виртуальных функций в конструкторах/деструкторах для вызова методов производных классов. Это источник трудноуловимых багов.
✅ Вывод:
C++ строго следует правилам объектной модели: пока объект конструируется (или разрушается), он считается экземпляром того класса, конструктор которого выполняется в данный момент. Это поведение важно помнить при проектировании иерархий классов!
@cpluspluc
👍25❤7🔥6🖕1🎄1
🧠 Задача с подвохом для продвинутых C++ разработчиков
🔹 Уровень: Advanced
🔹 Темы:
📌 Условие
Рассмотрим следующий код:
❓ Вопросы
1. Что будет выведено на экран?
2. Почему
3. Что изменится, если заменить
🔍 Разбор
✅ Ожидаемый вывод:
🔧 Почему так происходит
-
-
- Временный объект уничтожается (вызывается `Dtor`).
-
🔄 Если заменить `reserve(3)` на `resize(3)`
-
-
- Это может привести к копированию или перемещению уже созданных элементов.
⚠️ Подвох
Многие ошибочно считают, что
🧠 Вывод
-
-
- Не путай эти методы — от этого зависит и производительность, и семантика.
📌 Совет:
Если не хочешь лишнего перемещения, используй
@cpluspluc
🔹 Уровень: Advanced
🔹 Темы:
std::vector
, управление памятью, конструкторы/деструкторы, reserve()
vs resize()
📌 Условие
Рассмотрим следующий код:
#include <iostream>
#include <vector>
struct Foo {
Foo() { std::cout << "Ctor\n"; }
~Foo() { std::cout << "Dtor\n"; }
Foo(const Foo&) { std::cout << "Copy\n"; }
Foo(Foo&&) noexcept { std::cout << "Move\n"; }
};
int main() {
std::vector<Foo> v;
v.reserve(3); // Резервируем место под 3 элемента
std::cout << "--- Pushing ---\n";
for (int i = 0; i < 3; ++i) {
v.push_back(Foo());
}
std::cout << "--- Done ---\n";
}
❓ Вопросы
1. Что будет выведено на экран?
2. Почему
reserve()
не предотвращает конструкторы копирования/перемещения? 3. Что изменится, если заменить
reserve(3)
на resize(3)
?🔍 Разбор
✅ Ожидаемый вывод:
--- Pushing ---
Ctor
Move
Dtor
Ctor
Move
Dtor
Ctor
Move
Dtor
--- Done ---
Dtor
Dtor
Dtor
🔧 Почему так происходит
-
Foo()
создаёт временный объект. -
push_back()
вызывает перемещающий конструктор Move
. - Временный объект уничтожается (вызывается `Dtor`).
-
reserve(3)
выделяет память, но не создаёт объектов.🔄 Если заменить `reserve(3)` на `resize(3)`
-
resize(3)
создаст 3 объекта Foo через конструктор по умолчанию. -
push_back(Foo())
добавит четвёртый, возможно вызовет realocation. - Это может привести к копированию или перемещению уже созданных элементов.
⚠️ Подвох
Многие ошибочно считают, что
reserve(n)
создаёт n
объектов. Но это не так — reserve()
только выделяет память, не вызывая конструкторы. Именно поэтому внутри push_back
всё равно происходит перемещение или копирование.🧠 Вывод
-
reserve()
— экономия на реаллокациях, без создания объектов. -
resize()
— создаёт n
объектов, вызывает конструкторы. - Не путай эти методы — от этого зависит и производительность, и семантика.
// Резервирует память, не создаёт объекты
v.reserve(10);
// Создаёт 10 объектов Foo
v.resize(10);
📌 Совет:
Если не хочешь лишнего перемещения, используй
emplace_back()
:
v.emplace_back(); // Вызывает конструктор Foo напрямую внутри вектора
@cpluspluc
👍16❤4🔥1
🧠 Хитрая задача на C++: "Исчезающее число среди строк"
Условие
Дан список строк, представляющих числа от 1 до 100 включительно. Одного числа нет.
Твоя задача — найти, какого именно числа не хватает.
Нельзя использовать сортировку,
Пример:
Массив: ["1", "2", "3", ..., "98", "99"] (без "100")
Ответ: 100
Формат:
Решение с XOR
Пример использования:
Почему работает:
XOR — идеальное решение, когда нужно найти одну потерянную величину среди уникальных значений.
(XOR всех от 1 до 100) ^ (XOR из данных) = отсутствующее число.
@cpluspluc
Условие
Дан список строк, представляющих числа от 1 до 100 включительно. Одного числа нет.
Твоя задача — найти, какого именно числа не хватает.
Нельзя использовать сортировку,
std::accumulate
, std::unordered_map
, std::stoi
можно.Пример:
Массив: ["1", "2", "3", ..., "98", "99"] (без "100")
Ответ: 100
Формат:
int findMissingNumber(const std::vector<std::string>& data);
Решение с XOR
#include <iostream>
#include <vector>
#include <string>
int findMissingNumber(const std::vector<std::string>& data) {
int xor_full = 0;
int xor_data = 0;
for (int i = 1; i <= 100; ++i) {
xor_full ^= i;
}
for (const auto& s : data) {
xor_data ^= std::stoi(s);
}
return xor_full ^ xor_data;
}
Пример использования:
int main() {
std::vector<std::string> data;
for (int i = 1; i <= 100; ++i) {
if (i != 57) { // Удалим 57
data.push_back(std::to_string(i));
}
}
std::cout << "Пропущено: " << findMissingNumber(data) << std::endl;
return 0;
}
Почему работает:
XOR — идеальное решение, когда нужно найти одну потерянную величину среди уникальных значений.
a ^ a = 0
, 0 ^ b = b
. Поэтому: (XOR всех от 1 до 100) ^ (XOR из данных) = отсутствующее число.
@cpluspluc
👍16🔥4❤3🤔2
🔍 C++ Задача для профи: кто вызовется?
У тебя есть следующий код:
❓ Вопросы:
1. Что напечатает программа?
2. Почему вызов
3. Что произойдёт, если передать
🧠 Подвох:
-
-
- У тебя может возникнуть разный вывод в зависимости от перегрузки
✅ Разбор:
- `process(42)` → `Integral: 42` ✅
- `process(3.14)` → `Floating-point: 3.14` ✅
- `process('A')` → `Integral: 65` (ASCII код символа!) ⚠️
- `process(true)` → `Integral: 1` (да, это `bool`) ✅
🎯 Что проверяет задача:
- Понимание `SFINAE` и `enable_if`
- Типовую систему C++ (что именно считается integral)
- Разницу между `char`, `bool`, `int`, `float` в шаблонах
- Предсказание поведения перегрузки на уровне типов
@cpluspluc
У тебя есть следующий код:
#include <iostream>
#include <type_traits>
template<typename T>
typename std::enable_if<std::is_integral<T>::value, void>::type
process(T value) {
std::cout << "Integral: " << value << std::endl;
}
template<typename T>
typename std::enable_if<std::is_floating_point<T>::value, void>::type
process(T value) {
std::cout << "Floating-point: " << value << std::endl;
}
int main() {
process(42); // (1)
process(3.14); // (2)
process('A'); // (3)
}
❓ Вопросы:
1. Что напечатает программа?
2. Почему вызов
process('A')
может удивить?3. Что произойдёт, если передать
true
?🧠 Подвох:
-
char
— это integral type, а не string
и не отдельный класс-
true
— это тоже int`-подобный тип: `std::is_integral<bool>::value == true
- У тебя может возникнуть разный вывод в зависимости от перегрузки
✅ Разбор:
- `process(42)` → `Integral: 42` ✅
- `process(3.14)` → `Floating-point: 3.14` ✅
- `process('A')` → `Integral: 65` (ASCII код символа!) ⚠️
- `process(true)` → `Integral: 1` (да, это `bool`) ✅
🎯 Что проверяет задача:
- Понимание `SFINAE` и `enable_if`
- Типовую систему C++ (что именно считается integral)
- Разницу между `char`, `bool`, `int`, `float` в шаблонах
- Предсказание поведения перегрузки на уровне типов
@cpluspluc
👍12❤2🔥2
🔍 C++ Задача для профи: кто вызовется?
У тебя есть следующий код:
❓ Вопросы:
1. Что напечатает программа?
2. Почему вызов
3. Что произойдёт, если передать
🧠 Подвох:
-
-
- У тебя может возникнуть разный вывод в зависимости от перегрузки
✅ Разбор:
-
-
-
-
🎯 Что проверяет задача:
- Понимание
- Типовую систему C++ (что именно считается integral)
- Разницу между
- Предсказание поведения перегрузки на уровне типов
@cpluspluc
У тебя есть следующий код:
#include <iostream>
#include <type_traits>
template<typename T>
typename std::enable_if<std::is_integral<T>::value, void>::type
process(T value) {
std::cout << "Integral: " << value << std::endl;
}
template<typename T>
typename std::enable_if<std::is_floating_point<T>::value, void>::type
process(T value) {
std::cout << "Floating-point: " << value << std::endl;
}
int main() {
process(42); // (1)
process(3.14); // (2)
process('A'); // (3)
}
❓ Вопросы:
1. Что напечатает программа?
2. Почему вызов
process('A')
может удивить?3. Что произойдёт, если передать
true
?🧠 Подвох:
-
char
— это integral type, а не string
и не отдельный класс-
true
— это тоже int`-подобный тип: `std::is_integral<bool>::value == true
- У тебя может возникнуть разный вывод в зависимости от перегрузки
✅ Разбор:
-
process(42)
→ Integral: 42
✅-
process(3.14)
→ Floating-point: 3.14
✅-
process('A')
→ Integral: 65
(ASCII код символа!) ⚠️-
process(true)
→ Integral: 1
(да, это `bool`) ✅🎯 Что проверяет задача:
- Понимание
SFINAE
и enable_if
- Типовую систему C++ (что именно считается integral)
- Разницу между
char
, bool
, int
, float
в шаблонах- Предсказание поведения перегрузки на уровне типов
@cpluspluc
❤12👍6🔥3
⚙️ Задача для C++ разработчиков: «Непонятная ошибка, которая портит данные»
🎯 Цель: Найти и объяснить причину скрытого неопределённого поведения, которое проявляется не сразу
📍 Ситуация:
Ты разрабатываешь кроссплатформенное приложение на C++17, которое обрабатывает массивы бинарных данных.
На тестах — всё работает. Но у части пользователей (особенно на Linux) возникают:
- Повреждённые файлы после сериализации
- Непредсказуемые вылеты при больших объёмах данных
- Валидация данных случайно "съезжает" (байты путаются)
Вот фрагмент кода:
🔍 Визуально всё нормально. В unit-тестах — ок. На CI — ок.
Но на проде данные иногда повреждены, и никто не может воспроизвести баг стабильно.
🧩 Задача:
1. Почему
2. Что может отличаться на разных платформах и влиять на поведение?
3. Как бы ты безопасно сериализовал структуру в
4. Как это можно поймать с помощью
5. Как написать cross-platform-safe сериализацию?
💡 Подсказка:
В C++ `struct Packet` может иметь **padding** и **alignment**, которые отличаются на архитектурах. `memcpy` по `sizeof(Packet)` может захватить лишние или мусорные байты.
🛠 Решение:
1. `struct Packet` не является POD-структурой с гарантированным layout — в ней может быть **неинициализированный padding**, который `memcpy` тоже копирует.
2. Проблема усиливается на системах с разным выравниванием: x86 vs ARM, GCC vs MSVC.
3. Более безопасный способ — сериализовать поля по отдельности:
std::vector<uint8_t> serialize(const Packet& p) {
std::vector<uint8_t> buffer;
buffer.insert(buffer.end(), reinterpret_cast<const uint8_t*>(& p.id ),
reinterpret_cast<const uint8_t*>(& p.id ) + sizeof( p.id ));
buffer.insert(buffer.end(), p.data , p.data + sizeof( p.data ));
return buffer;
}
4. Или использовать `std::ostringstream` / `std::span` / `protobuf` / `flatbuffers`.
5. Проверка с `-fsanitize=undefined` даст warning:
```
memcpy: reading padding bytes from stack frame
```
📌 **Вывод:**
В C++ `memcpy` на структуру — это **ловушка**, если ты не контролируешь padding. Никогда не сериализуй структуры напрямую через память, если это не `#pragma pack` и не строго определённый layout.
💬 Это вопрос для собеседования на позицию C++ системного разработчика с уклоном в безопасность и низкоуровневую разработку.
@cpluspluc
🎯 Цель: Найти и объяснить причину скрытого неопределённого поведения, которое проявляется не сразу
📍 Ситуация:
Ты разрабатываешь кроссплатформенное приложение на C++17, которое обрабатывает массивы бинарных данных.
На тестах — всё работает. Но у части пользователей (особенно на Linux) возникают:
- Повреждённые файлы после сериализации
- Непредсказуемые вылеты при больших объёмах данных
- Валидация данных случайно "съезжает" (байты путаются)
Вот фрагмент кода:
#include <vector>
#include <cstring>
struct Packet {
uint32_t id;
char data[64];
};
std::vector<uint8_t> serialize(const Packet& p) {
std::vector<uint8_t> buffer(sizeof(Packet));
std::memcpy(buffer.data(), &p, sizeof(Packet));
return buffer;
}
🔍 Визуально всё нормально. В unit-тестах — ок. На CI — ок.
Но на проде данные иногда повреждены, и никто не может воспроизвести баг стабильно.
🧩 Задача:
1. Почему
memcpy
здесь небезопасен, хотя кажется логичным? 2. Что может отличаться на разных платформах и влиять на поведение?
3. Как бы ты безопасно сериализовал структуру в
std::vector<uint8_t>
? 4. Как это можно поймать с помощью
valgrind
/ asan
/ -fsanitize=undefined
? 5. Как написать cross-platform-safe сериализацию?
💡 Подсказка:
🛠 Решение:
1. `struct Packet` не является POD-структурой с гарантированным layout — в ней может быть **неинициализированный padding**, который `memcpy` тоже копирует.
2. Проблема усиливается на системах с разным выравниванием: x86 vs ARM, GCC vs MSVC.
3. Более безопасный способ — сериализовать поля по отдельности:
std::vector<uint8_t> buffer;
buffer.insert(buffer.end(), reinterpret_cast<const uint8_t*>(&
reinterpret_cast<const uint8_t*>(&
buffer.insert(buffer.end(),
return buffer;
}
4. Или использовать `std::ostringstream` / `std::span` / `protobuf` / `flatbuffers`.
5. Проверка с `-fsanitize=undefined` даст warning:
```
memcpy: reading padding bytes from stack frame
```
📌 **Вывод:**
В C++ `memcpy` на структуру — это **ловушка**, если ты не контролируешь padding. Никогда не сериализуй структуры напрямую через память, если это не `
💬 Это вопрос для собеседования на позицию C++ системного разработчика с уклоном в безопасность и низкоуровневую разработку.
@cpluspluc
👍29❤7🔥7😁1
🧠 C++ Задача для продвинутых: загадка с `std::move` и `const`
❓ Задача: что выведет этот код и почему?
🔍 Варианты ответов:
• a)
• b)
• c) Ошибка компиляции
• d) Поведение зависит от компилятора
💡 Разбор:
Смотрим на перегрузки
•
•
👉 Вызовется вторая функция, с
✅ Ответ: b) const lvalue reference
🧠 Вывод:
📌 Совет: перед использованием
❓ Задача: что выведет этот код и почему?
#include <iostream>
#include <string>
#include <utility>
std::string identity(std::string&& s) {
std::cout << "rvalue reference\n";
return std::move(s);
}
std::string identity(const std::string& s) {
std::cout << "const lvalue reference\n";
return s;
}
int main() {
const std::string a = "hello";
std::string b = identity(std::move(a));
std::cout << "Result: " << b << std::endl;
}
🔍 Варианты ответов:
• a)
rvalue reference
• b)
const lvalue reference
• c) Ошибка компиляции
• d) Поведение зависит от компилятора
💡 Разбор:
a
объявлена как const std::string
. Даже после std::move(a)
она остаётся const
, потому что std::move
не снимает константность, он лишь преобразует к rvalue:
std::move(const std::string&) → const std::string&&
Смотрим на перегрузки
identity
:•
identity(std::string&&)
— принимает неконстантный rvalue •
identity(const std::string&)
— принимает константный lvalueconst std::string&&
не подходит к std::string&&
, потому что нельзя привязать rvalue-ссылку к `const`-объекту без соответствия типов.👉 Вызовется вторая функция, с
const std::string&
✅ Ответ: b) const lvalue reference
🧠 Вывод:
std::move
не делает объект мутабельным. Если объект const
, он остаётся const
, и rvalue-перегрузки не срабатывают.📌 Совет: перед использованием
std::move
всегда учитывайте `const`-квалификатор. Он может "сломать" ожидаемую семантику перемещения.👍18❤5🔥3
🚀 Хотите мониторить загрузку вашей NVIDIA GPU прямо из C++?
Вот минимальный пример на C++, который использует
🛠 Требования:
• Установленный драйвер NVIDIA и утилита
• C++17 или выше
📄 Код:
📦 Компиляция:
bash
Копировать
Редактировать
g++ gpu_monitor.cpp -o gpu_monitor -std=c++17
📌 Вывод:
🧠 Подходит для:
• Мониторинга GPU в реальном времени
• Интеграции в бэкенды, боты, системы логгинга
• Лёгкой отладки ML/AI-приложений на C++
Вот минимальный пример на C++, который использует
nvidia-smi
через системный вызов для получения текущей загрузки GPU и использованной памяти.🛠 Требования:
• Установленный драйвер NVIDIA и утилита
nvidia-smi
• C++17 или выше
📄 Код:
#include <iostream>
#include <cstdlib>
#include <memory>
#include <array>
void get_gpu_utilization() {
std::array<char, 128> buffer;
std::string result;
std::unique_ptr<FILE, decltype(&pclose)> pipe(
popen("nvidia-smi --query-gpu=utilization.gpu,memory.used,memory.total --format=csv,nounits,noheader", "r"),
pclose
);
if (!pipe) {
std::cerr << "❌ Ошибка при вызове nvidia-smi" << std::endl;
return;
}
int gpu_id = 0;
while (fgets(buffer.data(), buffer.size(), pipe.get()) != nullptr) {
std::string line(buffer.data());
size_t pos1 = line.find(',');
size_t pos2 = line.rfind(',');
std::string util = line.substr(0, pos1);
std::string mem_used = line.substr(pos1 + 1, pos2 - pos1 - 1);
std::string mem_total = line.substr(pos2 + 1);
std::cout << "🖥 GPU " << gpu_id++ << ": " << util << "% load | "
<< mem_used << " MiB / " << mem_total << " MiB";
}
}
int main() {
get_gpu_utilization();
return 0;
}
📦 Компиляция:
bash
Копировать
Редактировать
g++ gpu_monitor.cpp -o gpu_monitor -std=c++17
📌 Вывод:
🖥 GPU 0: 17% load | 512 MiB / 8192 MiB
🧠 Подходит для:
• Мониторинга GPU в реальном времени
• Интеграции в бэкенды, боты, системы логгинга
• Лёгкой отладки ML/AI-приложений на C++
🥴15🔥14🤣5🥰3❤2😁2👾2👻1🙉1
🔥 Совет дня: быстро посчитать частоты элементов в C++ с `std::unordered_map`
Когда нужно подсчитать, сколько раз каждый элемент встречается в векторе — не пиши вручную поиск. Просто используй
📌 Удобно для:
— анализа строк
— парсинга логов
— простых хэш-агрегаций
@cpluspluc
Когда нужно подсчитать, сколько раз каждый элемент встречается в векторе — не пиши вручную поиск. Просто используй
unordered_map
:
#include <iostream>
#include <vector>
#include <unordered_map>
int main() {
std::vector<std::string> items = {"apple", "banana", "apple", "orange", "banana", "apple"};
std::unordered_map<std::string, int> freq;
for (const auto& item : items) {
++freq[item];
}
for (const auto& [key, count] : freq) {
std::cout << key << ": " << count << "\n";
}
// 👉 apple: 3, banana: 2, orange: 1
}
📌 Удобно для:
— анализа строк
— парсинга логов
— простых хэш-агрегаций
@cpluspluc
❤18😴4🙈1🗿1
🧠 C++ Задача для продвинутых: безопасный счётчик с автоматическим сбросом
Условие:
Реализуйте потокобезопасный счётчик, который:
1. Увеличивается при вызове
2. Возвращает текущее значение при
3. Автоматически сбрасывается в 0 через
4. Не сбрасывается, если за это время пришёл новый
🔧 Условия:
- Нельзя использовать сторонние библиотеки (только стандарт C++17+)
- Нельзя вручную управлять потоками (используйте
- Нужно обеспечить корректный RAII (автоматическое завершение потоков при разрушении объекта)
🔍 Пример:
💡 Подсказка:
Нужно реализовать "отложенный сброс", который перезапускается при каждом increment(). Используйте фоновый поток и condition_variable.
✅ Ожидаемое решение (упрощённый скелет):
🎯 Отличная задача, чтобы потренировать:
понимание RAII
работу с condition_variable
атомарные переменные
и правильную остановку фоновых потоков
Можешь доработать под конкретные кейсы — например, логирование событий сброса или работу с несколькими счётчиками.
Условие:
Реализуйте потокобезопасный счётчик, который:
1. Увеличивается при вызове
increment()
2. Возвращает текущее значение при
get()
3. Автоматически сбрасывается в 0 через
N
миллисекунд после последнего increment()4. Не сбрасывается, если за это время пришёл новый
increment()
🔧 Условия:
- Нельзя использовать сторонние библиотеки (только стандарт C++17+)
- Нельзя вручную управлять потоками (используйте
std::thread
, std::mutex
, `std::condition_variable`)- Нужно обеспечить корректный RAII (автоматическое завершение потоков при разрушении объекта)
🔍 Пример:
SafeCounter counter(1000); // сброс через 1000 мс
counter.increment(); // value = 1
std::this_thread::sleep_for(500ms);
counter.increment(); // value = 2, таймер сброса перезапускается
std::this_thread::sleep_for(1500ms);
std::cout << counter.get(); // value = 0
💡 Подсказка:
Нужно реализовать "отложенный сброс", который перезапускается при каждом increment(). Используйте фоновый поток и condition_variable.
✅ Ожидаемое решение (упрощённый скелет):
#include <iostream>
#include <thread>
#include <atomic>
#include <mutex>
#include <condition_variable>
#include <chrono>
class SafeCounter {
public:
SafeCounter(int timeout_ms) : timeout(timeout_ms), value(0), stop(false) {
worker = std::thread([this] { this->watch(); });
}
~SafeCounter() {
{
std::lock_guard<std::mutex> lock(mtx);
stop = true;
}
cv.notify_all();
worker.join();
}
void increment() {
{
std::lock_guard<std::mutex> lock(mtx);
++value;
last_action = std::chrono::steady_clock::now();
}
cv.notify_all();
}
int get() const {
return value.load();
}
private:
void watch() {
std::unique_lock<std::mutex> lock(mtx);
while (!stop) {
cv.wait_for(lock, timeout, [this] {
return stop || std::chrono::steady_clock::now() - last_action >= timeout;
});
if (stop) break;
if (std::chrono::steady_clock::now() - last_action >= timeout) {
value = 0;
}
}
}
std::chrono::milliseconds timeout;
std::chrono::steady_clock::time_point last_action = std::chrono::steady_clock::now();
std::atomic<int> value;
std::mutex mtx;
std::condition_variable cv;
std::thread worker;
bool stop;
};
🎯 Отличная задача, чтобы потренировать:
понимание RAII
работу с condition_variable
атомарные переменные
и правильную остановку фоновых потоков
Можешь доработать под конкретные кейсы — например, логирование событий сброса или работу с несколькими счётчиками.
❤17👍5🔥2
🧠 Задача: Реализация `TypeList` с поддержкой операций на этапе компиляции
📌 Описание
Реализуйте шаблонный класс
1. Получение длины списка (`length`)
2. Получение типа по индексу (`at<N>`)
3. Добавление типа в начало списка (`push_front<T>`)
4. Удаление первого типа (`pop_front`)
5. Проверка наличия типа в списке (`contains<T>`)
6. Фильтрация по условию (например, только целочисленные типы) (`filter<Predicate>`)
Всё это должно работать на этапе компиляции, без использования
🧩 Пример использования
🛠 Требования к реализации
Используйте только возможности шаблонов и constexpr.
Не используйте std::tuple, std::array, if constexpr (если хотите усложнить — можно).
Предпочтительно использование C++17 или выше.
Код должен компилироваться и проходить все static_assert.
🧪 Бонусное задание
Реализуйте print_types() — функцию, которая выводит все типы из списка в std::cout (можно использовать typeid, PRETTY_FUNCTION или другие хаки).
@cpluspluc
📌 Описание
Реализуйте шаблонный класс
TypeList
, который представляет собой список типов на этапе компиляции (compile-time type list). Он должен поддерживать следующие операции:1. Получение длины списка (`length`)
2. Получение типа по индексу (`at<N>`)
3. Добавление типа в начало списка (`push_front<T>`)
4. Удаление первого типа (`pop_front`)
5. Проверка наличия типа в списке (`contains<T>`)
6. Фильтрация по условию (например, только целочисленные типы) (`filter<Predicate>`)
Всё это должно работать на этапе компиляции, без использования
std::tuple
или других runtime-контейнеров.🧩 Пример использования
#include <type_traits>
#include <iostream>
// Пример предиката
template<typename T>
struct is_integral : std::is_integral<T> {};
int main() {
using MyList = TypeList<int, char, float, double, short>;
static_assert(MyList::length == 5);
static_assert(std::is_same_v<MyList::at<0>, int>);
static_assert(std::is_same_v<MyList::at<2>, float>);
using WithBool = MyList::push_front<bool>;
static_assert(WithBool::length == 6);
static_assert(std::is_same_v<WithBool::at<0>, bool>);
using Popped = WithBool::pop_front;
static_assert(std::is_same_v<Popped, MyList>);
static_assert(MyList::contains<int>);
static_assert(!MyList::contains<bool>);
using OnlyIntegral = MyList::filter<is_integral>;
static_assert(std::is_same_v<OnlyIntegral, TypeList<int, char, short>>);
return 0;
}
🛠 Требования к реализации
Используйте только возможности шаблонов и constexpr.
Не используйте std::tuple, std::array, if constexpr (если хотите усложнить — можно).
Предпочтительно использование C++17 или выше.
Код должен компилироваться и проходить все static_assert.
🧪 Бонусное задание
Реализуйте print_types() — функцию, которая выводит все типы из списка в std::cout (можно использовать typeid, PRETTY_FUNCTION или другие хаки).
@cpluspluc
❤8🔥4👍2