C++ geek
3.58K subscribers
254 photos
3 videos
18 links
Учим C/C++ на примерах
Download Telegram
🧵 Сегодня покажу вам простой способ логгировать вызовы функций в C++ — пригодится для отладки и анализа кода.

Часто бывает нужно понять, какие функции вызываются, в каком порядке и с какими параметрами. Вручную вставлять std::cout — неудобно. Вместо этого используем RAII-макрос с выводом в консоль:


#include <iostream>
#include <string>

struct FunctionLogger {
std::string func_name;
FunctionLogger(const std::string& name) : func_name(name) {
std::cout << ">> Entering: " << func_name << '\n';
}
~FunctionLogger() {
std::cout << "<< Exiting: " << func_name << '\n';
}
};

#define LOG_FUNCTION() FunctionLogger logger(__FUNCTION__)


Теперь в любой функции достаточно просто написать LOG_FUNCTION();, и вы получите автоматический лог при входе и выходе:


void do_work() {
LOG_FUNCTION();
// Работаем...
}


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

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

➡️ @cpp_geek
👍162👎1🤔1
Move-семантика: где можно ловко сэкономить

Многие знают про std::move, но не всегда используют его там, где это реально ускоряет код. Простой пример — возврат локального объекта из функции:


#include <string>

std::string make_string() {
std::string s = "Hello";
return s; // RVO или move
}


С C++17 тут почти всегда RVO (Return Value Optimization). Но если RVO невозможен (например, возвращаем тернарный оператор), компилятор применит move:


std::string make_string(bool flag) {
std::string a = "foo", b = "bar";
return flag ? a : b; // тут будет move
}


А вот так можно подсказать компилятору явно:


return std::move(flag ? a : b);


Но осторожно: не делайте std::move для локальной переменной в простом return — это может сломать RVO и привести к лишнему перемещению.

Ещё полезно помнить: move не всегда бесплатный. Например, для std::vector он копирует указатель и размер, но не элементы. Для std::string — зависит от Small String Optimization: короткие строки перемещаются как копия.

Вывод: используйте std::move там, где явно хотите отдать объект, а не копировать. Но не злоупотребляйте им: компилятор с C++17 сам неплохо справляется.

➡️ @cpp_geek
👍4
RAII — твой лучший друг (и почему не стоит бояться умных указателей)

Старый добрый new / delete — это классика, но и источник утечек, крашей и боли. В современном C++ ручное управление памятью почти всегда антипаттерн.

Решение — RAII (Resource Acquisition Is Initialization): ресурсы живут ровно столько, сколько объект, который ими владеет. Ушёл объект из области видимости — ресурс освободился.

Пример с умными указателями:


#include <memory>
#include <iostream>

struct Foo {
Foo() { std::cout << "Init\n"; }
~Foo() { std::cout << "Destroy\n"; }
};

void bar() {
std::unique_ptr<Foo> p = std::make_unique<Foo>(); // RAII
// делаем что-то
} // тут автоматически вызовется ~Foo()


Что важно знать:

* std::unique_ptr — владение в единственном числе, идеально для большинства случаев.
* std::shared_ptr — разделённое владение (но дороже по производительности).
* Никогда не делай new без обёртки — почти всегда лучше std::make_unique или std::make_shared.

RAII работает не только для памяти: файлы, мьютексы, сокеты — всё. Достаточно обернуть ресурс в класс с деструктором.

Профит: меньше багов, меньше утечек, чище код.

➡️ @cpp_geek
3👍3
std::exchange — простой способ менять значения и возвращать старые

Вместо того чтобы писать руками:


auto old = value;
value = new_value;
return old;


В modern C++ есть готовый инструмент - std::exchange (C++14+).


#include <utility>
#include <string>
#include <iostream>

int main() {
std::string s = "Hello";
auto old = std::exchange(s, "World");

std::cout << "old = " << old << ", s = " << s << '\n';
}


Вывод:


old = Hello, s = World


Когда полезно:

- Реализация move-конструкторов/операторов:


MyType(MyType&& other)
: data_(std::exchange(other.data_, nullptr)) {}

- Сброс состояния объектов и возвращение старого значения.
- Реализация одноразовых флагов (`once_flag` паттерн).

Плюсы:

- Одна строка вместо трёх.
- Читаемость выше — сразу видно, что мы заменяем значение и берём старое.

Помни: по умолчанию второе значение копируется/перемещается, так что это не нулевой по стоимости вызов.

➡️ @cpp_geek
👍31