III Flashcards
Синтаксис имен переменных, венгерская нотация
- Имена переменных в C++ могут состоять из букв и цифр, но не могут начинаться с цифр, а также содержать пробелы и арифметические операторы (такие, как +, - и т.п.).
- Именами переменных не могут быть зарезервированные ключевые слова. Например, переменная по имени return приведет к ошибке при компиляции.
- В именах переменных можно использовать символ подчеркивания, который позволяет создавать более понятные, самодокументируемые имена переменных.
- Вы можете встретить код C++, в котором имя переменной предваряется символами, описывающими тип переменной. Это соглашение называется венгерской нотацией и часто используется в программировании в Windows. Так, переменная firstNumber в венгерской нотации имела бы имя iFirstNumber, где префикс i означает тип int. Глобальная переменная имела бы имя giFirstNumber. Венгерская нотация в последние годы теряет популярность, частично из-за улучшения интегрированных сред разработки, при необходимости отображающих тип переменной, например при наведении на них указателя мыши.
Проблемы с использованием глобальных переменных
Безосновательное использование глобальных переменных обычно считается плохой практикой программирования.
Это связано с тем, что значение глобальной переменной может быть присвоено в любой функции, и это значение может оказаться непредсказуемым, в особенности если разные функциональные модули разрабатываются разными программистами группы или выполняются в разных потоках.
Концепция знаковых и беззнаковых целых чисел, самый старший бит
- Область памяти размером 1 байт может хранить одно из 2 в степени 8, т.е. 256 разных значений. Аналогично область памяти размером 16 битов может хранить одно из 2 в степени 16 разных значений, т.е. одно из 65536 уникальных значений.
- Но как же представить в этой же области отрицательные числа? Один из способов — “пожертвовать” одним из разрядов для хранения знака, который указывал бы, положительное или отрицательное значение содержится в других битах. Такой знаковый разряд имеет смысл делать самым старшим битом (Most-Significant-Bit — MSB). Если старший бит содержит информацию о знаке, предполагается, что значение 0 означает положительное число, а значение 1 — отрицательное. Другие биты содержат абсолютное значение числа.
- Таким образом, занимающее 8 битов знаковое число может содержать значения в пределах от -128 до 127, а занимающее 16 битов — значения в пределах от -32 768 до 32 767.
Беззнаковые целочисленные типы unsigned short, unsigned int, unsigned long и unsigned long long
- В отличие от знаковых аналогов, беззнаковые целочисленные типы не могут содержать информацию о знаке, зато могут содержать вдвое большие положительные значения.
- Переменные беззнакового типа используются тогда, когда ожидаются только неотрицательные значения. Так, если вы подсчитываете количество яблок, не используйте тип int; воспользуйтесь типом unsigned int. Последний может содержать вдвое больше положительных значений, чем первый.
- Размер переменной знакового и беззнакового типов одинаков; единственным различием этих двух типов является знаковый старший бит.
Переполнение типов
- Типы данных, такие как short, int, long, unsigned short, unsigned int, unsigned long и другие, имеют ограниченную емкость и могут содержать числа, не превышающие некоторые пределы. Превысив предел для выбранного типа, вы получаете переполнение.
- Возьмем в качестве примера unsigned short. Этот тип данных обычно содержит 16 бит, а потому может содержать значения от 0 до 65 535. Если вы прибавите 1 к 65535 в переменной типа unsigned short, циклический переход приведет к значению 0.
Использование разделителя разрядов
Определение размера переменной с использованием оператора sizeof
Размер представляет собой объем памяти, резервируемый компилятором при объявлении программистом переменной для хранения присваиваемых ей данных.
Размер переменной зависит от ее типа, и в языке C++ есть очень удобный оператор sizeof, который возвращает размер переменной или типа в байтах.
Применение оператора sizeof очень простое. Чтобы определить размер целого числа, вызовите оператор sizeof с параметром в виде типа int:
cout << “Размер int: “ << sizeof (int);
Запрет сужающего преобразования при использовании инициализации списком
При инициализации переменной меньшего целочисленного типа (скажем, short) значением переменной большего типа (скажем, int) вы рискуете получить ошибку сужающего преобразования, при которой компилятор должен преобразовать значение, хранящееся в типе, который потенциально может содержать гораздо большие числа, в тип, который имеет меньшие размеры, например:
- int largeNum = 5000000;*
- short smallNum = largeNum; // Компилируется, но возможна ошибка*
Чтобы избежать этой проблемы, C++11 рекомендует инициализацию списком, которая предотвращает сужение. Для использования этой возможности поместите значения или переменные инициализации в фигурные скобки { }. Синтаксис инициализации списком выглядит следующим образом:
- int largeNum = 5000000;*
- short anotherNum{ largeNum }; // Ошибка сужения!*
- int anotherNum{ largeNum }; // OK!*
- float someFloat{ largeNum }; // Ошибка! Возможно сужение*
- float someFloat{ 5000000 }; // OK! 5000000 помещается в float*
Автоматический вывод типа с использованием auto
- В ряде случаев тип переменной очевиден — по присваиваемому при инициализации значению. Например, если переменная инициализируется значением true, следует ожидать, что, скорее всего, типом переменной будет bool. Компиляторы с поддержкой C++11 и выше дают возможность определять тип неявно, с использованием вместо типа переменной его ключевого слова auto:
- auto coinFlippedHeads = true;*
- Здесь задача определения конкретного типа переменной coinFlippedHeads оставлена компилятору. Компилятор просто проверяет природу значения, которым инициализируется переменная, а затем выбирает тип, наилучшим образом подходящий для этой переменной.
- Использование ключевого слова auto требует инициализации переменной, поскольку компилятор нуждается в инициализирующем значении, чтобы принять решение о наилучшем типе для переменной. Если вы не инициализируете переменную, то применение ключевого слова auto приведет к ошибке при компиляции.
- Хотя, на первый взгляд, ключевое слово auto кажется не особенно полезным, оно существенно упрощает программирование в тех случаях, когда тип переменной сложен.
Использование ключевого слова typedef для замены типа
Язык C++ позволяет переименовывать типы переменных так, как вам кажется более удобным. Для этого используется ключевое слово typedef. Например, программист хочет назначить типу unsigned int более описательное имя STRICTLY_POSITIVE_INTEGER.
typedef unsigned int STRICTLY_POSITIVE_INTEGER;
STRICTLY_POSITIVE_INTEGER numEggsInBasket = 4532;
При компиляции первая строка указывает компилятору, что STRICLY_POSITIVE_INTEGER — это не что иное, как тип unsigned int. Впоследствии, когда компилятор встречает уже определенный тип STRICLY_POSITIVE_INTEGER, он заменяет его типом unsigned int и продолжает компиляцию.
Что такое константа
После того как значение константы определено, оно не может быть изменено. Попытки присваивания значения константе в языке C++ приводят к ошибке при компиляции.
Таким образом, в C++ константы похожи на переменные, за исключением того, что они не могут быть изменены. Подобно переменной, константа также занимает пространство в памяти и имеет имя для идентификации адреса выделенной для нее области. Однако содержимое этой области не может быть перезаписано. В языке C++ возможны следующие константы.
- Литеральные константы.
- Константы, объявленные с использованием ключевого слова const.
- Константные выражения, использующие ключевое слово constexpr (нововведение С++11).
- Константы перечислений, использующие ключевое слово enum.
- Константы, определенные с помощью макроопределений, использование которых не рекомендуется и осуждается.
Литеральные константы
Литеральные константы могут быть многих типов — целочисленные, строки и т.д.
В нашей первой программе строка “Hello World” выводится с помощью следующей инструкции:
std : : cout << “Hello World” << std : : endl;
Здесь “Hello World” — это константа строкового литерала (string literal). Когда вы объявляете целое число наподобие
int someNumber = 10;
целочисленной переменной someNumber присваивается начальное значение, равное 10. Здесь 10 — это часть кода, компилируемая в приложение, которая является неизменной и тоже является литеральной константой (literal constant).
Объявление переменных как констант с использованием ключевого слова const
Самый важный тип констант C++ с практической и программной точек зрения объявляется с помощью ключевого слова const, расположенного перед типом переменной. В общем виде объявление выглядит следующим образом:
const имя_типа имя_константы = значение;
Хорошей практикой программирования является определение переменных, значения которых предполагаются неизменными, как констант. Применение ключевого слова const указывает, что программист позаботился об обеспечении неизменности данных и защищает свое приложение от непреднамеренных изменений этой константы. Это особенно полезно, когда над проектом работает несколько программистов.
Константы полезны при объявлении массивов постоянной длины, которые неизменны во время компиляции.
Объявление констант с использованием ключевого слова constexpr
Ключевое слово constexpr позволяет объявлять константы подобно функциям:
constexpr double GetPi( ) {return 3.1415926;}
Одно constexpr - выражение может использовать другое:
constexpr double TwicePi( ) {return 2 * GetPi( );}
GetPi( ) и TwicePi( ) могут казаться функциями, но это не совсем функции. Дело в том, что функции вызываются во время выполнения программы. Эти же константные выражения компилятор заменяет числом 3.141593 при каждом использовании GetPi( ) и числом 6.283186 при использовании TwicePi( ). Такое разрешение TwicePi( ) в константу увеличивает скорость выполнения программы по сравнению выполнением вычисления, содержащегося в функции.
Перечисления
Иногда некая переменная должна принимать значения только из определенного набора. Например, вы не хотите, чтобы среди цветов радуги случайно оказался бирюзовой или среди направлений компаса оказалось направление влево. В обоих этих случаях необходим тип переменной, значения которой ограничиваются определенным вами набором.
Перечисления (enumerations) — это именно то, что необходимо в данной ситуации. Перечисления объявляются с помощью ключевого слова enum.
Вот пример перечисления, которое определяет цвета радуги:
enum RainbowColors
{
Violet = 0,
Indigo,
Blue,
Green,
Yellow,
Orange,
Red
};
А вот другой пример — направления компаса:
enum CardinalDirections
{
North,
South,
East,
West
};
Перечисления используются как пользовательские. Переменные этого типа могут принимать значения, ограниченные объявленными ранее значениями перечисления. Так, при определении переменной, которая содержит цвет радуги, вы объявляете ее следующим образом:
RainbowColors MyWorldsColor = Blue; // Начальное значение
При объявлении перечисления компилятор преобразует его константы, такие как Violet и другие, в целые числа. Каждое последующее значение перечисления на единицу больше предыдущего. Начальное значение вы можете задать сами, но если вы этого не сделаете, компилятор начнет счет с 0. Так, значению North соответствует числовое значение 0.
По желанию можно также явно определить числовое значение напротив каждой из перечисляемых констант при их инициализации.