Программирование на Си-плюс-плюс: ТОП-10 трюков

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

Препроцессор

Макрос на определение размера массива:

template  
char (&ArraySizeHelper(T (&array)[N]))[N];
#define arraysize(array) (sizeof(ArraySizeHelper(array)))

Это лучше, чем обычный sizeof(array)/sizeof(array[0]), потому что вызывает краш при компиляции, если переданный массив является просто указателем или нулевым указателем, тогда как более простые макросы беззвучно возвращают бесполезное значение. Подробный пример см. в статье PVS-Studio vs Chromium.

Предопределенные макросы:

#define expect(expr) if(!expr) cerr << "Assertion " << #expr \
" failed at " << __FILE__ << ":" << __LINE__ << endl;
 
#define stringify(x) #x
#define tostring(x) stringify(x)
#define MAGIC_CONSTANT 314159
 
cout << "Value of MAGIC_CONSTANT=" << tostring(MAGIC_CONSTANT);

Макрос tostring – это общий трюк, конвертирующий значение переменной в строку. Ядро Linux использует множество подобных макросов.

Использование итераторов для быстрого сброса содержимого контейнера:

#define dbg(v) copy(v.begin(), v.end(), ostream_iterator<typeof(*v.begin())>(cout, " "))

Шаблон Voodoo

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

Например:

template
struct Choose {
  enum {value = (n * Choose<n-1, r-1>::value) / r};
};
 
template
struct Choose<n, 0> {
  enum {value = 1};
};
 
int main() {
    cout << Choose<8, 3>::value;
    int x[Choose<25, 3>::value];
}

Больше интересного по ссылке.

Менеджмент памяти и RAII

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

Подробнее.

Программирование на Си-плюс-плюс и URI в коде

Вы можете поместить URI в свой код на C++, и компилятор не выдаст ошибку.

#include 
int main() {
using namespace std;
http://www.google.com
int x = 5;
cout << x;
}

Любой идентификатор, за которым следует двоеточие, становится меткой goto в Си-плюс-плюс. Все, что следует за двойным слешем, воспринимается как комментарий. Именно поэтому в приведенном выше коде http – это метка, а //google.com/ – комментарий. Но компилятор может выдать предупреждение, так как заданная метка не используется.

Упрощаем дебаг

Определите оператор  << для структур STL, чтобы упростить добавление отладочных выходов в ваш код. Это лучше, чем стандартные функции вывода. Также определите соответствующий макрос. Пример для C++:

#include 
#include 
#include 


#ifdef NDEBUG
#define DEBUG(var)
#else
#define DEBUG(var) { std::cout << #var << ": " << (var) << std::endl; }
#endif
template
std::ostream& operator<< (std::ostream& out, const std::map<T1,T2> &M) {
    out << "{ ";
    for (auto item:M) out << item.first << "->" << item.second << ", ";
    out << "}";

Ключевое слово auto

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

Стандартный вариант:

vector vs;
vs.push_back(4),vs.push_back(7),vs.push_back(9),vs.push_back(10);
for (vector::iterator it = vs.begin(); it != vs.end(); ++it)
    cout << *it << ' ';cout<<'\n';

Используем:

vector vs;
vs.push_back(4),vs.push_back(7),vs.push_back(9),vs.push_back(10);
for (auto it: vs)
    cout << it << ' ';cout<<endl;

  vector vs;
vs.push_back(4),vs.push_back(7),vs.push_back(9),vs.push_back(10);
for (auto& it: vs) it*=3;
for (auto it: vs)
    cout << it << ' ';cout<<endl;

Объявление переменной:

template
auto mult(A x, B y) -> decltype(x * y){
    return x * y;
}
int main(){
auto a = 3 * 2; //the return type is the type of operator (x*y) 
cout<<a<<endl;
    return 0;
}

Парные трюки

pair<int, int> p;
//Это
p = make_pair(1, 2);
//тождественно этому
p = {1, 2};
 
//Так что
pair<int, pair<char, long long> > p;
//теперь проще
p = {1, {'a', 2ll}};

Супер подключение

Просто используйте:

#include <bits/stdc++.h>

Эта библиотека включает в себя множество библиотек, которые нам нужны. Например, algorithm, iostream, vector и многие другие. Теперь вам не придется мучиться с запоминанием большого количества обязательных подключений.

Выводы

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

Дополнительная литература

Также рекомендуем Вам посмотреть:

Что такое мультиметоды в C++?
Подборка книг по C++ для любого уровня

ЛУЧШИЕ СТАТЬИ ПО ТЕМЕ

matyushkin
29 марта 2020

ТОП-10 книг по C++: от новичка до профессионала

Книги по C++ на русском языке с лучшими оценками. Расставлены в порядке воз...
Библиотека программиста
31 января 2019

Лучшие инструменты и советы начинающему C++ программисту

Хотите изучать C++? Делимся важными навыками, фреймворками и советами, кото...