Энтони Уильямс - Параллельное программирование на С++ в действии. Практика разработки многопоточных программ
- Название:Параллельное программирование на С++ в действии. Практика разработки многопоточных программ
- Автор:
- Жанр:
- Издательство:ДМК Пресс
- Год:2012
- Город:Москва
- ISBN:978-5-94074-448-1
- Рейтинг:
- Избранное:Добавить в избранное
-
Отзывы:
-
Ваша оценка:
Энтони Уильямс - Параллельное программирование на С++ в действии. Практика разработки многопоточных программ краткое содержание
Книга «Параллельное программирование на С++ в действии» не предполагает предварительных знаний в этой области. Вдумчиво читая ее, вы научитесь писать надежные и элегантные многопоточные программы на С++11. Вы узнаете о том, что такое потоковая модель памяти, и о том, какие средства поддержки многопоточности, в том числе запуска и синхронизации потоков, имеются в стандартной библиотеке. Попутно вы познакомитесь с различными нетривиальными проблемами программирования в условиях параллелизма.
Параллельное программирование на С++ в действии. Практика разработки многопоточных программ - читать онлайн бесплатно полную версию (весь текст целиком)
Интервал:
Закладка:
Если, удалив копирующие конструктор и оператор присваивания, вы явно напишете перемещающие конструктор и оператор присваивания, то класс будет допускать только перемещение — как, например, std::thread
и std::unique_lock<>
. В следующем листинге приведен пример такого класса.
Листинг А.2.Простой тип, допускающий только перемещение
class move_only {
std::unique_ptr data;
public:
move_only(const move_only&) = delete;
move_only(move_only&& other):
data(std::move(other.data)) {}
move_only& operator=(const move_only&) = delete;
move_only& operator=(move_only&& other) {
data = std::move(other.data);
return *this;
}
};
move_only m1; │
Ошибка, копирующий конструктор объявлен
move_only m2(m1);←┘
удаленным
move_only m3(std::move(m1));←┐
правильно, имеется переме-
│
щающий конструктор
Объекты, допускающие только перемещение, можно передавать функциям в качестве параметров и возвращать из функций, но если вы захотите переместить содержимое l -значения, то должны будете выразить свое намерение явно, воспользовавшись функцией std::move()
или оператором static_cast
.
Спецификатор = delete
можно задать для любой функции, а не только для копирующего конструктора и оператора присваивания. Тем самым вы ясно даете понять, что функция отсутствует. Но это еще не все — удаленная функция участвует в разрешении перегрузки, как любая другая, и вызывает ошибку компиляции, только если будет выбрана. Этим можно воспользоваться для исключения некоторых перегруженных вариантов. Например, если функция принимает параметр типа short
, то сужение типа int
можно предотвратить, написав перегруженный вариант, который принимает int
, и объявив его удаленным:
void foo(short);
void foo(int) = delete;
Любую попытку вызвать foo
с параметром типа int
компилятор встретит в штыки, так что вызывающей программе придётся явно привести параметр к типу short
:
foo(42); ←
Ошибка, перегрузка для int удалена
foo((short)42); ←
Правильно
А.3. Умалчиваемые функции
Если механизм удаленных функций позволяет явно объявить, что функция не реализована, то назначение умалчиваемых (defaulted) функций прямо противоположное - это средство указать, что компилятор должен автоматически сгенерировать реализацию функции «по умолчанию». Разумеется, это можно делать только для функций, которые компилятор и так генерирует: конструкторов, деструкторов, копирующих и перемещающих конструкторов, копирующих и перемещающих операторов присваивания.
Зачем это может понадобиться? Есть несколько причин.
• Чтобы изменить видимость функции. По умолчанию генерируемые компилятором функции открыты. Если требуется, чтобы они были защищенными или даже закрытыми, то писать их придётся самостоятельно. Но объявив функцию умалчиваемой, вы можете заставить компилятор сгенерировать ее и одновременно изменить уровень доступа.
• Для документирования. Если сгенерированной компилятором версии достаточно, то имеет смысл так прямо и сказать. Тогда всякий, кто впоследствии будет читать код, поймёт, что это сделано намеренно.
• Чтобы заставить компилятор сгенерировать функцию, которую в противном случае он не стал бы генерировать. Обычно это касается конструкторов по умолчанию, которые автоматически генерируются, только если нет ни одного определенного пользователем конструктора. Если вы хотите, например, определить свой копирующий конструктор, то, объявив конструктор по умолчанию умалчиваемым, заставите компилятор сгенерировать его.
• Чтобы сделать деструктор виртуальным и при этом генерируемым компилятором.
• Чтобы сгенерировать специальный вариант копирующего конструктора, например, принимающий параметр по неконстантной ссылке (по умолчанию генерируется конструктор, принимающий константную ссылку).
• Чтобы воспользоваться специальными свойствами сгенерированных компилятором функций, который теряются, если вы сами пишете реализацию. Подробнее об этом чуть ниже.
Умалчиваемые функции объявляются путем добавления спецификатора = default
, например:
class Y {
private:
Y() = default; ←
Изменяем видимость
public:
Y(Y&) = default; ←
Принимаем не-const ссылку
T& operator=(const Y&) = default;←┐
объявляем умалчиваемой
│
для документирования
protected:
virtual ~Y() = default; ←
Изменяем видимость и добавляем virtual
};
Выше я упомянул, что сгенерированные компилятором функции обладают специальными свойствами, которые невозможно получить от версии, написанной пользователем. Самое существенное отличие заключается в том, что сгенерированная компилятором функция может быть тривиальной . Отсюда вытекает ряд следствий.
• Объекты с тривиальными копирующим конструктором, копирующим оператором присваивания и деструктором можно копировать с помощью memcpy
или memmove
.
• Литеральные типы, используемые в constexpr
-функциях (см. раздел А.4) обязаны обладать тривиальными конструктором, копирующим конструктором и деструктором.
• Классы с тривиальными конструктором по умолчанию, копирующим конструктором, копирующим оператором присваивания и деструктором можно использовать в объединении ( union
), в котором определены пользовательские конструктор и деструктор.
• Классы с тривиальными конструктором копирующим оператором присваивания можно использовать вместе с шаблонным классом std::atomic<>
(см. раздел 5.2.6), то есть передавать значения такого типа атомарным операциям.
Одного объявления функции со спецификатором = default
недостаточно, чтобы сделать ее тривиальной, для этого класс должен удовлетворять всем прочим условиям, при которых соответствующая функция будет тривиальной. Однако явно написанная пользователем функция не будет тривиальной никогда .
Второе различие между классами с функциями, сгенерированными компилятором и написанными пользователем, заключается в том, что класс без написанных пользователем конструкторов может быть агрегатным и, стало быть, допускать инициализацию с помощью агрегатного инициализатора:
struct aggregate {
aggregate() = default;
aggregate(aggregate const&) = default;
int a;
double b;
};
aggregate x={42, 3.141};
В данном случае x.a
инициализируется значением 42
, a x.b
— значением 3.141
.
Третье различие малоизвестно и относится только к конструктору по умолчанию, да и то лишь в классах, удовлетворяющих определенному условию. Рассмотрим такой класс:
Читать дальшеИнтервал:
Закладка: