Энтони Уильямс - Параллельное программирование на С++ в действии. Практика разработки многопоточных программ
- Название:Параллельное программирование на С++ в действии. Практика разработки многопоточных программ
- Автор:
- Жанр:
- Издательство:ДМК Пресс
- Год:2012
- Город:Москва
- ISBN:978-5-94074-448-1
- Рейтинг:
- Избранное:Добавить в избранное
-
Отзывы:
-
Ваша оценка:
Энтони Уильямс - Параллельное программирование на С++ в действии. Практика разработки многопоточных программ краткое содержание
Книга «Параллельное программирование на С++ в действии» не предполагает предварительных знаний в этой области. Вдумчиво читая ее, вы научитесь писать надежные и элегантные многопоточные программы на С++11. Вы узнаете о том, что такое потоковая модель памяти, и о том, какие средства поддержки многопоточности, в том числе запуска и синхронизации потоков, имеются в стандартной библиотеке. Попутно вы познакомитесь с различными нетривиальными проблемами программирования в условиях параллелизма.
Параллельное программирование на С++ в действии. Практика разработки многопоточных программ - читать онлайн бесплатно полную версию (весь текст целиком)
Интервал:
Закладка:
struct X {
int а;
};
Если экземпляр класса X
создается без инициализатора, то содержащееся в нем значение ( а
) типа int
инициализируется по умолчанию . Если у объекта статический класс памяти, то значение инициализируется нулем, в противном случае начальное значение произвольно, что может привести к неопределённому поведению, если программа обращается к объекту раньше, чем ему будет присвоено значение:
X x1; ←
значение x1.a не определено
С другой стороны, если инициализировать экземпляр X
путем явного вызова конструктора по умолчанию, то он получит значение 0:
X x2 = X(); ←
x2.а == 0
Это странное свойство распространяется также на базовые классы и члены классов. Если в классе имеется сгенерированный компилятором конструктор по умолчанию, и каждый член самого класса и всех его базовых классов также имеет сгенерированный компилятором конструктор по умолчанию, то переменные-члены самого класса и его базовых классов, принадлежащие встроенным типам, также будут иметь неопределенное значение или будут инициализированы нулями в зависимости от того, вызывался ли явно для внешнего класса его конструктор по умолчанию.
У этого замысловатого и потенциально чреватого ошибками правила есть тем не менее применения, а, если вы пишете конструктор по умолчанию самостоятельно, то это свойство утрачивается; данные-члены (например, а
) либо всегда инициализируются (коль скоро вы указали значение или явно вызвали конструктор по умолчанию), либо вообще не инициализируются (если вы этого не сделали):
X::X() : а() {} ←
всегда а == 0
X::X() : а(42) {} ←
всегда а == 42
X::X() {} ←
(1)
Если инициализация а
при конструировании X
не производится (как в третьем примере (1)), то a
остается неинициализированным для нестатических экземпляров X
и инициализируется нулем для экземпляров X
со статическим временем жизни.
Обычно, если вы вручную напишете хотя бы один конструктор, то компилятор не станет генерировать конструктор по умолчанию. Стало быть, если он вам все-таки нужен, его придётся написать самостоятельно, а тогда это странное свойство инициализации теряется. Однако явно объявив конструктор умалчиваемым, вы можете заставить компилятор сгенерировать конструктор по умолчанию и сохранить это свойство:
X::X() = default;
Это свойство используется в атомарных типах (см. раздел 5.2), в которых конструктор по умолчанию явно объявлен умалчиваемым. У таких типов начальное значение не определено, если только не выполняется одно из следующих условий: (а) задан статический класс памяти (тогда значение инициализируется нулем); (b) для инициализации нулем явно вызван конструктор по умолчанию; (с) вы сами явно указали начальное значение. Отметим, что в атомарных типах конструктор для инициализации значением объявлен как constexpr
(см. раздел А.4), чтобы разрешить статическую инициализацию.
А.4. constexpr
-функции
Целые литералы, например 42
, — это константные выражения . Равно как и простые арифметические выражения, например 23*2-4
. Частью константного выражения могут быть также const
-переменные любого целочисленного типа, которые сами инициализированы константным выражением:
const int i = 23;
const int two_i = i * 2;
const int four = 4;
const int forty_two = two_i - four;
Помимо использования константных выражений для инициализации переменных, которые могут использоваться в других константных выражениях, есть ряд случаев, где разрешается применять только константные выражения.
• Задание границ массива:
int bounds = 99; │
Ошибка, bounds — не константное
int array[bounds];←┘
выражение
const int bounds2 = 99;│
Правильно, bounds2 — константное
int array2[bounds2]; ←┘
выражение
• Задание значения параметра шаблона, не являющего типом:
template
struct test {}; │
Ошибка, bounds —
│
не константное
test is;←┘
выражение
test ia2;←┐
Правильно, bounds2 —
│
константное выражение
• Задание непосредственно в определении класса инициализатора для переменной-члена класса целочисленного типа со спецификаторами static const
:
class X {
static const int the_answer = forty_two;
};
• Употребление в инициализаторах встроенных типов или агрегатов, применяемых для статической инициализации:
struct my_aggregate {
int a;
int b;
};
static my_aggregate ma1 =│
Статическая
{ forty_two, 123 }; ←┘
инициализация
int dummy = 257; │
Динамическая
static my_aggregate ma2 = {dummy, dummy};←┘
инициализация
Такая статическая инициализация полезна для предотвращения зависимости от порядка инициализации и состояний гонки.
Всё это не ново и было описано еще в стандарте С++ 1998 года. Но в новом стандарте появилось и дополнение в части константных выражений — ключевое слово constexpr
.
Ключевое слово constexpr
применяется главным образом как модификатор функции. Если параметр и возвращаемое функцией значение удовлетворяют определенным условиям, а тело функции достаточно простое, то в ее объявлении можно указать constexpr
и использовать функцию в константных выражениях. Например:
constexpr int square(int x) {
return x*x;
}
int array[square(5)];
В этом случае массив array
будет содержать 25 значений, потому что функция square
объявлена как constexpr
. Конечно, из того, что функцию можно использовать в константном выражении, еще не следует, что любой случай ее использования автоматически будет константным выражением:
int dummy = 4;
(1) Ошибка, dummy — не константное
int array[square(dummy)];←┘
выражение
В этом примере dummy
не является константным выражением (1), поэтому не является таковым и square(dummy)
. Это обычный вызов функции, и, следовательно, для задания границ массива array его использовать нельзя.
А.4.1. constexpr
и определенные пользователем типы
До сих пор мы употребляли в примерах только встроенные типы — такие, как int
. Но в новом стандарте С++ допускаются константные выражения любого типа, удовлетворяющего требованиям, предъявляемым к литеральному типу . Чтобы тип класса можно было считать литеральным, должны быть выполнены все следующие условия:
• в классе должен существовать тривиальный копирующий конструктор;
• в классе должен существовать тривиальный деструктор;
Читать дальшеИнтервал:
Закладка: