(495) 925-0049, ITShop интернет-магазин 229-0436, Учебный Центр 925-0049
  Главная страница Карта сайта Контакты
Поиск
Вход
Регистрация
Рассылки сайта
 
 
 
 
 

Ещё более современный C++

Не так давно Герб Саттер открыл на своём сайте новую страничку - Elements of Modern C++ Style, где он описывает преимущества нового стандарта и то, как они повлияют на код.

 Стандарт C++11 вносит множество новых фич. Далее мы рассмотрим те из них, благодаря которым C++11 можно считать новым языком по сравнению с C++98, т.е.:
Они меняют стиль Вашего кода, привносят новые идиомы. Они повлияют на архитектуру С++ библиотек. Например: умные указатели получат большее распространение (и как аргументы функций, и как их возвращаемые значения), как и функции, возвращающие большие объекты по значению.
Они будут использоваться столь часто и интенсивно, что Вы, вероятно, будете встречать их в каждом втором примере кода. Например: вряд ли какой-нибудь пример из пяти или более строчек кода обойдётся без упоминания ключевого слова auto.

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

auto

 Используйте ключевое слово auto везде, где это возможно.
 Во-первых, не стоит повторять имя типа, о котором и мы, и компилятор уже знаем:
// C++98
map<int,string>::iterator i = m.begin();
 
// C++11
auto i = begin(m);

 Во-вторых, так гораздо удобнее в случае, если тип имеет неизвестное или большое и неказистое имя, которое Вы даже произнести не в состоянии.
// C++98
binder2nd< greater<int> > x = bind2nd( greater<int>(), 42 );
 
// C++11
auto x = [](int i) { return i > 42; };

 Отметим, что использование auto не меняет смысла Вашего кода. Он по-прежнему статически типизирован, и каждое выражение по-прежнему обладает чётким и ясным типом. Просто язык больше не заставляет Вас повторяться, заново указывая тип.

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

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

 Всегда используйте стандартные умные указатели и невладеющие сырые указатели. Никогда не используйте владеющие сырые указатели и delete (кроме крайних случаев, вроде реализации низкоуровневой структуры).

 В общем случае указатель - это shared_ptr, выражающий общее владение.

// C++98
widget* pw = new widget();
:::
delete pw;
 
// C++11
auto pw = make_shared<widget>();

 Для разрыва циклических связей и выражения опциональности (например, реализации кеша объектов) используйте weak_ptr.
// C++11
class gadget;
 
class widget {
private:
    shared_ptr<gadget> g;
};
 
class gadget {
private:
    weak_ptr<widget> w;
};

 В случае, если у указателя должен быть единственный владелец, используйте unique_ptr, предназначенный специально для этого случая. Любое выражение вида "new T" должно немедленно инициализировать другой объект, владеющий им, обычно unique_ptr.
// C++11 Pimpl Idiom
class widget {
    :::
private:
    class impl;
    unique_ptr<impl> pimpl;
};
 
// в .cpp файле
class impl {
    :::
};
 
widget::widget()
    : pimpl( new impl() )
{
}

 Если Вам нужно работать с объектом, который может пережить Вас, используйте сырой указатель.
// C++11
class node {
 vector< unique_ptr<node> > children;
 node* parent;
public:
 :::
};

nullptr

 Всегда используйте для нулевого значения nullptr и никогда 0 или макрос NULL, поскольку они неоднозначны и могут быть как числом, так и указателем.
// C++98
int* p = 0;
 
// C++11
int* p = nullptr;

for и range

 С новым for'ом, умеющим работать с range, перебирать элементы контейнеров стало ещё проще и удобнее.
// C++98
for( vector<double>::iterator i = v.begin(); i != v.end(); ++i ) {
    total += *i;
}
 
// C++11
for( auto d : v ) {
    total += d;
}

begin и end

 Всегда используйте begin и end как нечлены, потому что такой подход легко расширяем и позволяет работать со всеми типами контейнеров, а не только теми, которые следуют стилю STL и предоставляют методы .begin() и .end().

 Если Вы используйте не-STL коллекцию, которую можно проитерировать, но не реализующую методы .begin() и .end(), Вы можете самостоятельно перегрузить begin и end для неё так, чтобы итерировать эту коллекцию так же, как и контейнеры STL. Такая перегрузка, например, уже реализована для массивов в стиле C.
vector<int> v;
int a[100];
 
// C++98
sort( v.begin(), v.end() );
sort( &a[0], &a[0] + sizeof(a)/sizeof(a[0]) );
 
// C++11
sort( begin(v), end(v) );
sort( begin(a), end(a) );

Лямбда функции

 Благодаря лямбдам Ваш код станет элегантнее и быстрее. С их появлением использовать существующие алгоритмы STL стало проще раз в 100. Новые библиотеки всё чаще полагаются на поддержку лямбда-функций (PPL), а некоторые и вовсе требуют их наличия (C++ AMP).

 Небольшой пример: найти первый элемент в v, который больше x и меньше y.
// C++98: пишем обычный цикл (использовать std::find_if сложно и непрактично)
vector<int>::iterator i = v.begin(); // i нам ещё пригодится
for( ; i != v.end(); ++i ) {
    if( *i > x && *i < y ) break;
}
 
// C++11: используем std::find_if
auto i = find_if( begin(v), end(v), [=](int i) { return i > x && i < y; } );

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

Move / &&

 Семантика переноса сильно изменит дизайн наших API. Гораздо чаще станет использоваться возвращение по значению.
// C++98: как избежать копирования
vector<int>* make_big_vector(); // вариант 1: возвращаем указатель: копирования нет, но не забудьте про delete
:::
vector<int>* result = make_big_vector();
void make_big_vector( vector<int>& out ); // вариант 2: выдача по ссылке: копирования нет, но вызывающему нужен именованный объект
:::
vector<int> result;
make_big_vector( result );
 
// C++11: перенос
vector<int> make_big_vector();
:::
vector<int> result = make_big_vector();

 Если Вы можете сделать что-то более эффективное, чем простое копирование, используйте перемещение.

И ещё

 В современном C++ ещё много хороших вещей. Очень много. И в ближайшем будущем я напишу о них подробнее.

 Но на текущий момент, всё вышенаписанное - это must-know. Эти фичи лежат в основе современного C++, определяя его стиль. И совсем скоро Вы сможете встретить их практически в каждом кусочке свеженаписанного C++ кода. Именно они делают современный C++ чистым, безопасным и быстрым - таким, которым индустрия будет полагаться долгие годы.



 Распечатать »
 Правила публикации »
  Написать редактору 
 Рекомендовать » Дата публикации: 01.11.2011 
 

Магазин программного обеспечения   WWW.ITSHOP.RU
Delphi Professional Named User
Enterprise Connectors (1 Year term)
Nero Basic Burning ROM 2018 VL 5 - 9 License corporate
JIRA Software Commercial (Cloud) Standard 10 Users
СУБД Линтер Бастион. Серверная лицензия. 5 клиентских подключений
 
Другие предложения...
 
Курсы обучения   WWW.ITSHOP.RU
 
Другие предложения...
 
Магазин сертификационных экзаменов   WWW.ITSHOP.RU
 
Другие предложения...
 
3D Принтеры | 3D Печать   WWW.ITSHOP.RU
 
Другие предложения...
 
Новости по теме
 
Рассылки Subscribe.ru
Информационные технологии: CASE, RAD, ERP, OLAP
Новости ITShop.ru - ПО, книги, документация, курсы обучения
Программирование на Microsoft Access
CASE-технологии
СУБД Oracle "с нуля"
Все о PHP и даже больше
Windows и Office: новости и советы
 
Статьи по теме
 
Новинки каталога Download
 
Исходники
 
Документация
 
 



    
rambler's top100 Rambler's Top100