Современный язык C++: что нужно знать разработчику

Язык C++ не стоит на месте – с 2011 года в нём произошли существенные изменения. Теперь это современный и непрерывно развивающийся язык.


Современный C++: что нужно знать разработчику

Но от этого он не стал легче. Программирование на C++ остаётся самым сложным среди широко используемых языков. Однако по сравнению с предыдущими версиями язык Cpp стал более удобным.

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

После появления этой фичи программирование на Cpp стало проще :)

Благодаря auto язык C++ устанавливает типы данных во время компиляции, избавляя от необходимости объявления каждого типа. Это очень удобно для типов вроде map<string,vector<pair<int,int>>>.

Современный язык C++: что нужно знать разработчику

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

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

Современный язык C++: что нужно знать разработчику

7 и 8 строки Cpp содержат инициализацию в фигурных скобках – это тоже новая функция, которую несёт язык программирования C++ 11.

Помните, что для использования auto нужно указать компилятору значение типа.

А что случится, если написать auto a = {1, 2, 3} ? Вызовет ошибку компиляции, или это будет вектором?

На самом деле в C++11 введены std::initializer_list<type>. Список инициализации в фигурных скобках будет рассматриваться как легковесный контейнер, если объявлено auto.

Вывод типов компилятором будет полезен для сложных структур данных:

Современный язык C++: что нужно знать разработчику

Не забудьте проверить строку 25! Выражение auto [v1,v2] = itr.second – новинка в C++17. Оно называется структурированным связыванием. В предыдущих версиях языка нужно было извлекать каждую переменную отдельно. Но структурированное связывание делает этот процесс удобнее.

Более того, если нужно получить данные по ссылке, просто добавьте символ: auto &[v1,v2] = itr.second.

Лямбда-выражения

Язык C++11 поддерживает лямбда-выражения – что-то вроде анонимных функций в JavaScript. Они являются функциональными объектами без имён и принимают переменные в различных областях видимости, основываясь на кратком синтаксисе. Они могут быть присвоены переменным.

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

Современный язык C++: что нужно знать разработчику

Кроме инициализации в фигурных скобках здесь интересны begin() и end() – универсальные шаблоны, также появившиеся в C++11. Затем следует лямбда-функция, которая сравнивает данные. Параметры лямбда-функции объявляет auto, которое добавлено в язык C++14. До этого нельзя было использовать auto для параметров функций.

Заметьте, что лямбда-выражение начинается в квадратных скобках []. Они определяют область видимости лямбды – сколько полномочий она имеет над локальными переменными и объектами.

Примеры списков захвата для лямбда-выражений

  • [] – ничего не захватывает. Нельзя использовать локальные переменные внешней области видимости внутри лямбда-выражения. Можно использовать только параметры.
  • [=] – захватывает локальные объекты в области видимости по значению. Можно использовать локальные переменные и параметры, но не изменять.
  • [&]  – захватывает локальные объекты в области видимости по ссылкам. Можно изменять локальные переменные и параметры, как в примере выше.
  • [this] – захватить этот указатель по значению.
  • [a, &b] – захватить объекты a по значению, b по ссылке.

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

lambda C++

В этом примере, если локальные переменные захвачены по значению ([factor]) в лямбда-выражении, то поменять factor в 5 строке не получится. У вас просто нет на это прав. Не злоупотребляйте своими правами! ?

Заметьте, что мы используем val как ссылку. Это гарантирует, что любое изменение внутри лямбда-функции изменяет vector.

Операторы init внутри if и switch

init C++

Теперь можно инициализировать переменные и проверять условие одновременно внутри блоков if и switch.  Это помогает держать код кратким и чистым. Общая форма:

if( init-statement(x); condition(x)) {
    // сделать здесь что-то
} else {
    // здесь область видимости x
    // сделать здесь что-то
}

constexpr

Допустим, у вас есть выражение для оценки, и его значение не меняется после инициализации. Можно предварительно рассчитать значение, а затем использовать его в качестве макро. Или воспользоваться предложением C++11 – использовать constexpr.

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

constexpr C++

Код выше – типичное использование constexpr.

Так как мы объявили функцию вычисления чисел Фибоначчи как constexpr, компилятор может рассчитать fib(20) во время компиляции. Так что после компиляции он поменяет строку const long long bigval = fib(20); на const long long bigval = 2432902008176640000;.

Заметьте, что переданный аргумент – это значение const. Это важный момент функций, объявленных constexpr – передаваемый аргумент должен быть также constexpr или const. Иначе функция поведёт себя как нормальная функция, что означает отсутствие предварительных расчётов во время компиляции.

Переменные тоже могут быть constexpr. В этом случае такие переменные должны вычисляться во время компиляции. Иначе вы получите ошибку.

Позже в C++ 17 были представлены constexpr-if and constexpr-lambda.

Кортежи

Так же как pairtuple – это коллекция фиксированных значений разных типов данных.

tuple C++

Иногда удобней использовать std::array вместо tuple. array ближе к типу массива чистого C, но с функциональностью стандартной библиотеки C++. Эта структура данных добавлена  в C++ 11.

Дедукция аргумента шаблона класса

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

Следовательно:

std::pair<std::string, int> user = {"M", 25}; // предыдущая версия
std::pair user = {"M", 25}; // C++17

Тип дедукции задаётся неявно. Это удобно для tuple.

// предыдущая версия
std::tuple<std::string, std::string, int> user ("M", "Chy", 25);
// дедукция в действии! 
std::tuple user2("M", "Chy", 25);

Умные указатели

Из-за свободы, которую язык C++ предоставляет разработчикам, в нём легко «выстрелить себе в ногу». И во многих случаях во всём виноваты указатели.

К счастью, в C++ 11 представлены умные указатели, которые удобней своих «сырых» предшественников. Они помогают программистам предотвращать утечки памяти, освобождая её, когда это возможно. Они же обеспечивают безопасность исключений.

Использование умных указателей избавляет от необходимости явного вызова удаления. Умный указатель – это класс-обёртка вокруг указателя с перегруженными операторами * и  ->. Объекты класса умных указателей выглядят как указатели, но могут делать такие недоступные для обычных указателей вещи, как автоматическое уничтожение, подсчёт ссылок и прочее.

Идея в том, чтобы сделать класс с указателем, деструктором и с перегруженными операторами * и  ->. Поскольку деструктор вызывается автоматически при выходе объекта из области видимости, динамически распределённая память удаляется автоматически.

#include<iostream> 
using namespace std; 

class SmartPtr 
{ 
int *ptr; // Актуальный указатель 
public: 
// Конструктор 
explicit SmartPtr(int *p = NULL) { ptr = p; } 

// Деструктор 
~SmartPtr() { delete(ptr); } 

// Перегрузка отвязанного оператора 
int &operator *() { return *ptr; } 
}; 

int main() 
{ 
	SmartPtr ptr(new int()); 
	*ptr = 20; 
	cout << *ptr; 

	// Нам не нужно вызывать delete ptr: когда объект 
	// ptr выходит из области видимости, его деструктор вызывается автоматически 
	// и диструктор удаляет ptr. 

	return 0; 
}
20

На этом всё! Напоследок держите годный репозиторий для отслеживания обновлений в C++.

Дополняйте список улучшений в комментариях!

МЕРОПРИЯТИЯ

Комментарии

ВАКАНСИИ

Добавить вакансию
Golang разработчик (middle)
от 230000 RUB до 300000 RUB
DevOps
Санкт-Петербург, от 150000 RUB до 400000 RUB

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