Константные (постоянные) переменные
До сих пор все переменные, которые мы видели, были непостоянными, то есть их значения можно изменить в любое время. Например:
Однако иногда бывает полезно определять переменные со значениями, которые нельзя изменить. Например, рассмотрим ускорение свободного падения у поверхности Земли: 9,8 м/с2. Маловероятно, что в ближайшее время оно изменится (а если это произойдет, у вас, вероятно, возникнут более серьезные проблемы, чем изучение C++). Определение этого значения как константы помогает гарантировать, что оно не будет случайно изменено.
Чтобы сделать переменную константой, просто поместите ключевое слово до или после типа переменной, например:
Хотя C++ принимает до или после типа, мы рекомендуем использовать константу перед типом, потому что это лучше соответствует соглашению обычного английского языка, согласно которому модификаторы ставятся перед изменяемым объектом (например, «green ball» (зеленый шар), а не «ball green» (шар зеленый)).
Константные переменные должны быть инициализированы, когда вы их определяете, после этого это значение не может быть изменено с помощью присваивания.
Объявление переменной как предотвращает непреднамеренное изменение ее значения:
Определение константной переменной без ее инициализации также вызовет ошибку компиляции:
Обратите внимание, что константные переменные могут быть инициализированы из других переменных (включая неконстантные):
часто используется с параметрами функции:
Задание параметра функции константой делает две вещи. Во-первых, это сообщает человеку, вызывающему функцию, что функция не изменит значение . Во-вторых, это гарантирует, что функция не изменит значение .
Когда аргументы передаются по значению, нас обычно не волнует, изменяет ли функция значение параметра (поскольку это всего лишь копия, которая в любом случае будет уничтожена в конце функции). По этой причине мы обычно не делаем константными параметры, передаваемые по значению. Но позже мы поговорим о других типах параметров функций (где изменение значения параметра приведет к изменению значения переданного аргумента)
Для этих типов параметров важно разумное использование
Использование символьных констант в программе
Во многих программах символьные константы используются часто (в разных местах кода). Это могут быть физические или математические константы (например, число Пи или число Авогадро), или специфические значения в вашей программе. Чтобы не писать их каждый раз, когда они необходимы — просто определите их в одном месте и используйте везде, где они нужны. Таким образом, если вам придется изменить их значения — достаточно будет зайти в один файл и внести в него правки, а не искать эти константы по всей программе.
Алгоритм использования символьных констант в вашей программе:
Шаг №1: Создайте заголовочный файл для хранения констант.
Шаг №2: В заголовочном файле объявите пространство имен.
Шаг №3: Добавьте все ваши константы в созданное пространство имен (убедитесь, что все константы имеют спецификатор ).
Шаг №4: #include заголовочный файл везде, где нужны константы.
Например, файл constants.h:
#ifndef CONSTANTS_H
#define CONSTANTS_H
// Определите собственное пространство имен для хранения констант
namespace constants
{
const double pi(3.14159);
const double avogadro(6.0221413e23);
const double my_gravity(9.2);
// … другие константы
}
#endif
1 |
#ifndef CONSTANTS_H // Определите собственное пространство имен для хранения констант namespaceconstants { constdoublepi(3.14159); constdoubleavogadro(6.0221413e23); constdoublemy_gravity(9.2); // … другие константы } |
Используйте оператор разрешения области видимости для доступа к константам в файлах .cpp:
#include «constants.h»
//…
double circumference = 2 * radius * constants::pi;
//…
1 |
#include «constants.h» doublecircumference=2*radius *constants::pi; //… |
Если в программе много констант разных типов, то сделайте отдельные файлы для каждого из этих типов. Таким образом, отделив математические константы от специфических значений (которые могут быть разными в разных программах), вы сможете подключать файл с математическими константами к любой другой программе.
Условная компиляция
Директивы #if или #ifdef/#ifndef вместе с директивами #elif, #else и #endif управляют компиляцией частей исходного файла.
Если указанное выражение после #if имеет ненулевое значение, в записи преобразования сохраняется группа строк, следующая сразу за директивой #if. Синтаксис условной директивы следующий:
1234567
#if константное выражение группа операций#elif константное выражение группа операций#else группа операций#endif
Отличие директив #ifdef/#ifndef заключается в том, что константное выражение может быть задано только с помощью #define.
У каждой директивы #if в исходном файле должна быть соответствующая закрывающая директива #endif. Между директивами #if и #endif может располагаться любое количество директив #elif, однако допускается не более одной директивы #else. Директива #else, если присутствует, должна быть последней перед директивой #endif.
Пример
1234567891011121314151617
#include <stdio.h>#include <stdlib.h>#define P 2int main(){ system(«chcp 1251»); system(«cls»);#if P==1 printf(«Выполняется ветка 1»);#elif P==2 printf(«Выполняется ветка 2, P=%d», P);#else printf(«Выполняется другая ветка, P=%d», P);#endif getchar(); return 0;}
Результат выполнения
Язык Си
7 ответов
Лучший ответ
Интересно, что я проверил это в своем приложении и получил ту же ошибку.
Я потратил некоторое время, проверяя заголовки, чтобы увидеть, есть ли что-нибудь, не определяющее , и ничего не нашел.
Итак, я переместил
Быть первым в моем файле (я не использую PCH, так что, если да, вам нужно будет иметь его после ), и вдруг он скомпилируется идеально.
Попробуйте переместить его выше по странице. Совершенно не уверен, почему это может вызвать проблемы.
Изменить : разобрался. встречается внутри заголовков cmath. Это означает, что что-то выше в списке #includes включает без указанного . специально разработан, чтобы вы могли снова включить его с этим определением, которое теперь изменено на добавление и т. Д. Это НЕ относится к . Поэтому вам нужно убедиться, что вы , прежде чем включать что-либо еще. Надеюсь, это проясняет вам это
Если не включить просто , вы используете нестандартный C / C ++, как уже указывалось
123
αλεχολυτ
12 Мар 2019 в 18:17
С CMake это было бы просто
В .
1
FLUXparticle
9 Сен 2020 в 10:54
Как было предложено пользователем 7860670, щелкните проект правой кнопкой мыши, выберите свойства, перейдите к C / C ++ -> Препроцессор и добавьте в определения препроцессора.
Вот что у меня сработало.
1
Den-Jason
18 Май 2020 в 12:40
Согласно документации Microsoft о математических константах:
Файл может быть косвенно включен в ваш проект. В моем случае один из возможных порядков включения был следующим:
1
αλεχολυτ
12 Мар 2019 в 17:04
Это по-прежнему проблема в VS Community 2015 и 2017 при создании консольных приложений или приложений для Windows. Если проект создается с предварительно скомпилированными заголовками, предварительно скомпилированные заголовки, по-видимому, загружаются до любого из #includes, поэтому даже если #define _USE_MATH_DEFINES является первой строкой, он не будет компилироваться. #including math.h вместо cmath не имеет значения.
Единственные решения, которые я могу найти, — это либо начать с пустого проекта (для простых консольных или встроенных системных приложений), либо добавить / Y- в аргументы командной строки, что отключает загрузку предварительно скомпилированных заголовков.
Для получения информации об отключении предварительно скомпилированных заголовков см., Например, https://msdn.microsoft.com/en-us/library/1hy7a92h. aspx
Было бы неплохо, если бы MS это изменила / исправила. Я преподаю вводные курсы программирования в большом университете, и объясняя это новичкам, я никогда не пойму, пока они не совершат ошибку и не будут бороться с ней в течение дня или около того.
4
user3533658
23 Мар 2018 в 03:17
Это работает для меня:
Компилирует и печатает как следует: .
Должно быть несовпадение кода, который вы разместили, и кода, который вы пытаетесь скомпилировать.
Убедитесь, что перед не вставляются предварительно скомпилированные заголовки.
10
rubenvb
3 Июл 2011 в 15:52
Рассмотрите возможность добавления переключателя / D_USE_MATH_DEFINES в командную строку компиляции или определения макроса в настройках проекта. Это перетащит символ во все доступные темные углы включаемых и исходных файлов, оставляя ваш исходный код чистым для нескольких платформ. Если вы установите его глобально для всего проекта, вы не забудете его позже в новом файле (ах).
15
Thinkeye
19 Фев 2014 в 16:52
Функции возведения в степень и логарифма
Эта функция принимает один параметр в виде дробного числа и возвращает .
Пример:
Вывод:
Эта функция работает так же, как и , но возвращает . Здесь, значит , то есть, .
Пример:
Вывод:
Функция log() — логарифм числа
Функция находит логарифм числа по основанию (по умолчанию). — параметр опциональный. Если нужно вычислить логарифм с определенным основанием, его нужно указать.
Пример:
Вывод:
Эта функция похожа на функцию логарифма, но добавляет к . значит , то есть, .
Пример:
Вывод:
Вычисляет логарифм по основанию 10.
Пример:
Вывод:
Функция pow() — степень числа
Используется для нахождение степени числа. Синтаксис функции . Она принимает два аргумента: основание и степень.
Пример:
Вывод:
Функция sqrt() — квадратный корень числа
Эта функция используется для нахождения квадратного корня числа. Она принимает число в качестве аргумента и находит его квадратный корень.
Пример:
Вывод:
Описание define
#define является одной из инструкций препроцессора ардуино и C++. Само слово препроцессор означает, что работа с такими инструкциями проходит до основного процесса компиляции кода. Во время такого «нулевого» этапа препроцессор компилятора пробегает по исходному коду, находит все наши инструкции и производит замену прямо в том же исходном коде. Все это делается незаметно от нас, мы результатов этого отдельного шага не видим. И только после работы препроцессора запускается сам компилятор, который будет анализировать и собирать код с учетом тех изменений, который уже сделал препроцессор.
В чем-то это напоминает механизм макросов и шаблонов. Расставив определенные с помощью #define шаблоны по всему коду, мы даем возможность компилятору автоматически заменить их перед компиляцией на то, что нам нужно. Единственное исключение – если идентификатор находится внутри скобок “”. Тогда подстановки не будет.
Все это нужно для того, чтобы во время создания программы тратить меньше времени на написание команд и их изменение. Мы можем «закодировать» коротким словом большую «длинную» конструкцию и писать в коде ее («короткую»), а не длинную. А компилятор сам подставит «длинную» в нужные места перед компиляцией. Также мы можем один раз изменить значение подстановки в начале программы, и новое значение само подставится во всех нужных местах. Возможностей использования #define – много. Как это работает – посмотрим на примерах ниже.
Плохое решение: использование объекто-подобных макросов с параметром подстановки в качестве символьных констант
Сначала мы покажем вам менее желательный способ определения символьной константы. Этот метод обычно использовался во многих старых кодах, поэтому вы всё еще можете его увидеть.
В уроке «2.9 – Знакомство с препроцессором» вы узнали, что у объекто-подобных макросов есть две формы: одна не принимает параметр подстановки (обычно используется для условной компиляции), а другая имеет параметр подстановки. Здесь мы поговорим о случае с параметром подстановки. Он имеет следующую форму:
Каждый раз, когда препроцессор встречает эту директиву, любое дальнейшее появление идентификатора заменяется на подставляемый_текст. Идентификатор традиционно набирается заглавными буквами с использованием подчеркивания для обозначения пробелов.
Рассмотрим следующий фрагмент:
Когда вы компилируете свой код, препроцессор заменяет все экземпляры литеральным значением 30, которое затем компилируется в ваш исполняемый файл.
Вы, вероятно, согласитесь, что это по нескольким причинам гораздо более интуитивно понятно, чем использование магического числа. даже без комментария предоставляет контекст для того, что программа пытается сделать. Во-вторых, если количество студентов в классе изменяется, нам нужно изменить значение только в одном месте, и все экземпляры при следующей компиляции будут заменены новым литеральным значением.
Рассмотрим наш второй пример с использованием символьных констант :
В этом случае очевидно, что и должны быть независимыми, даже если они имеют одно и то же значение (30). Таким образом, если нам нужно обновить размер класса, мы не сможем случайно изменить длину имени.
Так почему бы не использовать для создания символьных констант? Есть (по крайней мере) три основных проблемы.
Во-первых, поскольку макросы вычисляются препроцессором, который заменяет символьное имя определенным значением, символьные константы, определенные через , не отображаются в отладчике (который показывает ваш фактический код). Таким образом, хотя компилятор будет компилировать , в редакторе кода вы увидите , и не будет отслеживаться в отладчике. Вам нужно будет найти определение , чтобы узнать его фактическое значение. Это может затруднить отладку ваших программ.
Во-вторых, макросы могут конфликтовать с обычным кодом. Например:
Если в someheader.h появляется определение с помощью макроса с именем , эта простая программа сломается, так как препроцессор заменит имя целочисленной переменной на какое-то значение макроса.
В-третьих, макросы не подчиняются обычным правилам области видимости, что означает, что в редких случаях макрос, определенный в одной части программы, может конфликтовать с кодом, написанным в другой части программы, с которой он не должен был взаимодействовать.
Предупреждение
Избегайте использования для создания макросов символьных констант.
Функции представления чисел
ceil() и floor() — целая часть числа
и — функции общего назначения. Функция ceil округляет число до ближайшего целого в большую сторону. Функция floor убирает цифры десятичных знаков. Обе принимают десятичное число в качестве аргумента и возвращают целое число.
Пример:
Вывод:
Функция fabs() — абсолютное значение
Функция используется для вычисления абсолютного значения числа. Если число содержит любой отрицательный знак (), то функция убирает его и возвращает положительное дробное число.
Пример:
Вывод:
factorial() — функция факториала
Эта функция принимает положительное целое число и выводит его факториал.
Пример:
Вывод:
Примечание: при попытке использовать отрицательное число, возвращается ошибка значения ().
Пример:
Вывод:
Функция fmod() — остаток от деления
Функция возвращает . Разница в том, что выражение работает только с целыми числами, а эту функцию можно использовать и для чисел с плавающей точкой.
Пример:
Вывод:
Функция frexp()
Эта функция возвращает мантиссу и показатель степени в виде пары () любого числа , решая следующее уравнение.
Пример:
Вывод:
Функция fsum() — точная сумма float
Вычисляет точную сумму значений с плавающей точкой в итерируемом объекте и сумму списка или диапазона данных.
Пример:
Вывод:
8 ответов
На некоторых (особенно старых) платформах (см. Комментарии ниже) вам может потребоваться
, а затем включить необходимый файл заголовка:
, а значение пи может быть доступно через :
В моем (2014) он определен как:
, но проверьте свой , чтобы узнать больше. Выдержка из «старого» (в 2009 г.):
Однако:
-
на новых платформах (по крайней мере, на моем 64-битном Ubuntu 14.04) мне не нужно определять
-
На (недавних) платформах Linux есть значения, также предоставленные как расширение GNU:
ответ дан 22 November 2019 в 23:14
C++ 20
Наконец, это прибыло: http://eel.is/c++draft/numbers
я ожидаю, что использование будет похоже:
я дам ему попытку, когда поддержка прибудет в GCC, GCC 9.1.0 с все еще не поддерживает его.
принятое предложение описывает:
существует также , конечно,
Эти константы используют C++ 14 переменных шаблонных функций: C++ 14 Переменных Шаблонов: какова их цель? Какой-либо пример использования?
В более ранних версиях проекта, константа находилась под : http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p0631r7.pdf
ответ дан 22 November 2019 в 23:14
Pi можно вычислить как . Вы можете рассчитать значение таким образом и кэшировать его.
ответ дан 22 November 2019 в 23:14
Поскольку официальная стандартная библиотека не определяет константу PI, вам придется определить ее самостоятельно. Итак, ответ на ваш вопрос «Как я могу получить PI без определения его вручную?» это «Вы не делаете — или вы полагаетесь на некоторые специфичные для компилятора расширения.». Если вас не беспокоит переносимость, вы можете проверить это в руководстве к вашему компилятору.
C ++ позволяет писать
, но не гарантируется, что инициализация этой константы будет статической. Однако компилятор G ++ обрабатывает эти математические функции как внутренние и может вычислять это постоянное выражение во время компиляции.
ответ дан 22 November 2019 в 23:14
Стандартный C ++ не имеет константы для PI.
Многие компиляторы C ++ определяют в (или в для C) как нестандартное расширение. Возможно, вам придется , прежде чем вы его увидите.
ответ дан 22 November 2019 в 23:14
Вместо того, чтобы писать
, я бы рекомендовал использовать или в зависимости от вашего компилятора.
Таким образом, вы можете быть уверены, что даже в случае, если кто-то включит заголовок до вас (и без #define), у вас все равно будут константы вместо скрытая ошибка компилятора, на отслеживание которой потребуется много времени.
ответ дан 22 November 2019 в 23:14
Вы также можете использовать ускорение, которое определяет важные математические константы с максимальной точностью для запрошенного типа (например, float vs double).
Дополнительные примеры см. В документации по boost .
ответ дан 22 November 2019 в 23:14
Обычно я предпочитаю определять свое собственное: , потому что не все реализации предоставляют это для вас.
Вопрос о том, есть ли эта функция вызывается во время выполнения или статически выключается во время компиляции, обычно это не проблема, потому что в любом случае это происходит только один раз.
ответ дан 22 November 2019 в 23:14
Другие вопросы по тегам:
Макроподстановка #define, #undef
Define — обозначает «определять». Данная директива используется при присвоении имен выражениям. Объявляется #define в самом начале программного кода до работы с функциями обработки событий. По своему принципу работы очень напоминает объявление переменной и указание ей значения. Рассмотрим беспараметрическую форму:
C++
#define NUMBER 999
#define NUMBER 999 |
Мы объявили вхождение NUMBER и присвоили ему значение 999. Теперь в программном коде можно указывать слово NUMBER, которое будет выдавать нам заданное значение.
Допустим нам нужно объявить версию скрипта и потом выдать ее в значение свойства программы version, а также в коммент в функции:
C++
#define VER «1.02»
#property version VER
void OnStart()
{
Comment(«Версия программы: «,VER);
}
1 |
#define VER «1.02» void OnStart() Comment(«Версия программы: «,VER); } |
Версия программы это текстовая переменная, поэтому указываем ее в кавычках. Далее после того, как VER задали, ее просто прописываем в version вместо цифр, как обычно. Теперь при выводе информации в левый верхний угол экрана мы будем видеть текущую версию программы.
Другой пример, мы знаем, что при работе с типом ордеров, нулем обозначается ордер на покупку, а 1 — на продажу. Чтобы не смущать себя единицами и нулями, просто объявим вхождения BUY и SELL, т.к. они не заняты программными константами.
C++
#define BUY 0
#define SELL 1
void OnStart()
{
if(OrderType() == BUY) Print(«Ордер по покупку»);
else if(OrderType() == SELL) Print(«Ордер по продажу»);
}
1 |
#define BUY 0 void OnStart() if(OrderType()==BUY)Print(«Ордер по покупку»); else if(OrderType()==SELL)Print(«Ордер по продажу»); |
Понятно, что сейчас ордер у нас еще не выбран, поэтому данное выражение не совсем рабочее, но как пример сгодится.
Я часто использую данную директиву в работе со статическими массивами, когда точно знаю, сколько элементов они содержат, чтобы сразу видеть, какой элемент за что отвечает:
C++
#define ORDER 0
#define SYMBOL 1
#define PROFIT 2
void OnStart()
{
string array = { «Buy»,»EURUSD»,»12.1″ };
Print(array);
Print(array);
Print(array);
}
1 |
#define ORDER 0 void OnStart() string array={«Buy»,»EURUSD»,»12.1″}; Print(array); Print(array); Print(array); |
То есть вместо обычного array или array мы используем уже наименования, которые присвоили нулю, единице и двойке.
У данной директивы есть и более сложный вид — параметрическая форма. Лично я не нашел ей применения в моем коде, но не сказать о ней будет неправильно. Суть в том, чтобы для нескольких вхождений выполнить арифметическое действие, определенные также заранее.
C++
#define X 10
#define Y 5
#define SUM(a,b) (a + b)
void OnStart()
{
double sum = SUM(X,Y);
Print(sum); //результат 15
}
1 |
#define X 10 void OnStart() double sum=SUM(X,Y); Print(sum);//результат 15 } |
Формула расчета определена до начала работы с функцией. В самой функции уже подставлены объявленные значения X и Y в переменную sum. Аналогично это можно было бы представить следующим образом:
C++
double sum1 = X+Y;
Print(sum1); //результат 15
1 |
double sum1=X+Y; Print(sum1);//результат 15 |
Но если у вас будет длинная формула, то тогда такие действия выше себя оправдывают. При сложных формулах необходимо указывать выражения в скобках:
C++
#define Z 10-5
#define W 5+3
#define MULT(a,b) ((a)*(b))
void OnStart()
{
double mult = MULT(Z,W);
Print(mult); //результат 40
}
1 |
#define Z 10-5 void OnStart() double mult=MULT(Z,W); Print(mult);//результат 40 } |
Директива #undef нужна для отмены директивы #define. После вызова undef, она стирается из памяти программы.
C++
#define TEXT «ABC»
void OnStart()
{
Print(TEXT);
#undef TEXT
}
1 |
#define TEXT «ABC» void OnStart() Print(TEXT); #undef TEXT } |
Символьные константы
На предыдущем уроке мы говорили о — литералы, которые используются в программе в виде констант. «Поскольку использовать их не рекомендуется, то какой выход?» — спросите вы. А я отвечу: «Использовать символьные константы». Символьная константа — это тот же литерал (магическое число), только с идентификатором. Есть 2 способа объявления символьных констант в языке C++. Первый способ хороший, а второй — не очень. Рассмотрим сначала плохой способ.
Плохой способ: Использовать в качестве символьных констант.
Раньше этот способ широко использовался, так что вы все еще можете его увидеть в старых программах.
Как мы уже знаем, макросы-объекты имеют две формы: с и без . Рассмотрим первый вариант с . Он выглядит следующим образом:
Как только препроцессор встретит эту директиву, все дальнейшие появления будут заменены на . обычно пишется заглавными буквами с нижним подчёркиванием вместо пробелов.
Например:
#define MAX_STUDENTS_PER_CLASS 30
//…
int max_students = numClassrooms * MAX_STUDENTS_PER_CLASS;
//…
1 |
#define MAX_STUDENTS_PER_CLASS 30 intmax_students=numClassrooms *MAX_STUDENTS_PER_CLASS; //… |
Во время компиляции программы, препроцессор заменит все идентификаторы на литерал .
Согласитесь, это гораздо лучше, нежели использовать магические числа, как минимум, по нескольким причинам. дает понимание того, что это за значение и зачем оно используется (это понятно даже без комментариев). Во-вторых, если число нужно будет изменить — достаточно будет внести правки только в директиву #define, все остальные идентификаторы в программе будут автоматически заменены новым значением при повторной компиляции.
Рассмотрим еще один пример:
#define MAX_STUDENTS_PER_CLASS 30
#define MAX_NAME_LENGTH 30
int max_students = numClassrooms * MAX_STUDENTS_PER_CLASS;
setMax(MAX_NAME_LENGTH);
1 |
#define MAX_STUDENTS_PER_CLASS 30 intmax_students=numClassrooms *MAX_STUDENTS_PER_CLASS; setMax(MAX_NAME_LENGTH); |
Здесь понятно, что и не являются одним и тем же объектом, хоть и имеют одинаковые значения.
Так почему же этот способ плохой? На это есть две причины:
Во-первых, макросы обрабатываются препроцессором, который заменяет идентификаторы на определенные значения. Эти значения не отображаются в отладчике (во время отладки вашей программы). При компиляции в отладчике вы увидите . «А как тогда узнать значение ?» — спросите вы. А я отвечу: «Вам придется самостоятельно найти это в коде». А процесс поиска может занять некоторое время (в зависимости от размеров вашей программы).
Во-вторых, эти директивы всегда имеют глобальную область видимости (о ней мы поговорим позже). Это означает, что значения #define в одной части кода могут конфликтовать со значениями #define в другой части кода.
Правило: Не используйте директиву #define для создания символьных констант.
Хороший способ: Использовать переменные со спецификатором const.
Например:
const int maxStudentsPerClass { 30 };
const int maxNameLength { 30 };
1 |
constintmaxStudentsPerClass{30}; constintmaxNameLength{30}; |
Такие значения отображаются в отладчике, а также следуют всем правилам обычных переменных (в том числе и по области видимости).
Правило: Используйте спецификатор const для создания символьных констант.
Задание и использование констант
Выше было дано определение констант. Теперь рассмотрим работу с
константами более подробно.
Все константы вне зависимости от типа данных можно подразделить на
две категории: именованные константы и константы, которые не имеют
собственного имени. Например:
25
— константа целого
типа;
3.14
— вещественная константа;
‘A’
— символьная
константа.
Все три приведённые здесь константы не имеют имени, они заданы
своим внешним представлением и используются в программе
непосредственно, например так:
int k=25; // переменная
k инициализирована константой — целым
числом 25.
В ряде случаев
константе удобнее дать имя и использовать её далее по имени. Обычно
это делается для математических или физических констант.
В языке C был
единственный способ создания именованных
констант — с помощью
директивы препроцессора #define,
например:
#define PI 3.14
……………
double t;
t = PI * 2; // здесь
использована именованная константа PI, заданная
выше
В
языке C++ появился ещё один способ —
использование константных переменных,
то есть переменных, которые нельзя изменять после инициализации.
Рассмотрим на том же примере:
const double PI=3.14; // здесь
PI —
константная переменная
double t;
t=PI * 2;
В
чём преимущество от использования константных переменных вместо
задания констант с помощью директивы препроцессора #define?
Всё очень просто: при использовании константной переменной компилятор
проверяет правильность задания константы, и если она будет задана
неверно, то будет выдана ошибка именно в операторе, в котором дано
определение константной переменной.
Если использована именованная
константа, заданная директивой препроцессора #define,
то ошибка будет показана только там, где используется константа.
Например:
// в директиве
препроцессора сделаем ошибку:
#define PI ююю
…………..
double t;
t = PI * 2; // в этой строке
компилятор выдаст ошибку,
//
хотя на самом деле ошибка допущена гораздо раньше!
|
|
Включение файлов (#include)
Данная командная строка располагается также в начале программного кода, но может быть указана в любом месте. В большинстве случаев подключенные файлы относятся к файлам библиотек. Библиотеки могут уже находиться в каталоге программ, быть стандартными, либо же пользователь может подключить свои.
Попробуем для примера подключить библиотеку stderror.mqh, которая определяет имя директивы для номера ошибки.
C++
#include
void OnStart()
{
int error = GetLastError();
if(error == ERR_NO_ERROR) Print(«Ошибок нет»);
}
1 |
#include void OnStart() int error=GetLastError(); if(error==ERR_NO_ERROR)Print(«Ошибок нет»); |
В примере мы записали в переменную error номер ошибки, а далее проверили, чтобы ошибки не было, задав вместо нуля директиву ERR_NO_ERROR, которая уже определена в подключенной библиотеке. Чтобы посмотреть код данной библиотеки, нужно щелкнуть на нее барабанчиком мыши.
Самая используемая программная библиотека stdlib.mq4 создана для того, чтобы расшифровать ошибку. В примере выше мы сами писали текст ошибки, теперь подключим уже готовую библиотеку и выдадим принт:
C++
#include <..>
void OnStart()
{
int error = GetLastError();
string new_error = ErrorDescription(error);
Print(new_error); //результат no error
}
1 |
#include <..> void OnStart() int error=GetLastError(); string new_error=ErrorDescription(error); Print(new_error);//результат no error } |
Тут из подключенной библиотеке мы взяли функцию и внесли в нее номер ошибки. Т.к. ошибки нет, то результат принта «no error«.
Мы рассмотрели встроенные библиотеки MQL. Помимо них можно делать пользовательские наборы. Допустим вы в каждом своем советнике используете Трейлинг Стоп, либо перевод в безубыток. Чтобы не повторять строчки из кода в код, можно создать одну библиотеку с нужными функциями и вычислениями, и прикреплять ее к каждому необходимому программному коду.
Спецификатор constexpr
В языке C++ есть два вида констант:
Константы времени выполнения. Их значения определяются только во время выполнения программы. Переменные типа и выше являются константами времени выполнения, так как компилятор не может определить их значения во время компиляции. зависит от пользовательского ввода (который можно получить только во время выполнения программы), а зависит от значения, переданного в функцию (это значение также определится только во время выполнения программы).
Константы времени компиляции. Их значения определяются во время компиляции программы. Например, переменная со значением силы тяжести на Земле является константой времени компиляции, так как мы её определяем во время написания программы (до начала её выполнения).
В большинстве случаев не важно какой тип константы вы используете: времени выполнения или времени компиляции. Однако, все же есть несколько ситуаций, когда C++ может потребовать константу времени компиляции вместо времени выполнения (например, при определении длины массива фиксированного размера — мы рассмотрим это несколько позже)
Так как есть 2 типа констант, то компилятору нужно постоянно отслеживать, к какому из них относится какая переменная. Чтобы упростить это задание, в C++11 добавили спецификатор constexpr, который сообщает компилятору, что текущая переменная является константой времени компиляции:
constexpr double gravity (9.8); // ок, значение определяется во время компиляции программы
constexpr int sum = 4 + 5; // ок, результат выражения 4 + 5 определяется во время компиляции программы
std::cout << «Enter your age: «;
int age;
std::cin >> age;
constexpr int myAge = age; // неправильно, переменная age не определяется во время компиляции программы
1 |
constexpr doublegravity(9.8);// ок, значение определяется во время компиляции программы constexpr intsum=4+5;// ок, результат выражения 4 + 5 определяется во время компиляции программы std::cout<<«Enter your age: «; intage; std::cin>>age; constexpr intmyAge=age;// неправильно, переменная age не определяется во время компиляции программы |
Использовать его вы, скорее всего, не будете, но знать о нем не помешает.
Правило: Любая переменная, которая не должна изменять свое значение после инициализации, должна быть объявлена с помощью спецификатора const (или constexpr).