Evgenii Legotckoi
Evgenii Legotckoi05 червня 2017 р. 03:33

C++ - Підручник 003. Константи

Зміст

C++ підтримує дві нотації незмінності:

  1. const - яка має на увазі, що значення не змінюватиметься. Насамперед це використовується для специфікації інтерфейсів, для даних які передаються у функції та методи так, щоб не побоюватися, що вони будуть змінені. Компілятор відстежує наявність специфікаторів const;
  2. constexp - який має на увазі обчислення константи під час компіляції. Використовується для розміщення даних у пам'яті, де вони не будуть пошкоджені, а також для покращення продуктивності.

Наприклад:

const int dmv = 17;                      // константа с именем dmv
int var = 17;                            // переменная var не является константой
constexpr double max1 = 1.4∗square(dmv); // OK, так как square(17) является константым выражением
constexpr double max2 = 1.4∗square(var); // Ошибка, так как var не является константой
const double max3 = 1.4∗square(var);     // OK, поскольку выражение может быть вычислено в рантайме
double sum(const vector<double>&);       // sum не будет модифицировать его аргументы
vector<double> v {1.2, 3.4, 4.5};        // v не является константой
const double s1 = sum(v);                // OK, будет вычислено в рантайме
constexpr double s2 = sum(v);            // Ошибка, sum(v) не является константным выражением.

Щоб функція використовувалася в константному вираженні, тобто, обчислювалася компілятором, необхідно визначити її зі специфікатором constexpr .

constexpr double square(double x) { return x∗x; }

constexpr функція має бути досить простою, щоб обчислюватися компілятором, а також повертати обчислене значення. constexpr функції можуть викликатися неконстантими аргументами в контексті яких не потрібні константні вирази.

конст

Об'єкти зі специфікатором const не можуть бути змінені, а також мають бути ініціалізовані.

const int model = 90;           // model является константой
const int v[] = { 1, 2, 3, 4 }; // v[i] является константой
const int x;                    // Ошибка: значение не инициализировано

Оскільки об'єкти зі специфікаторів const не можуть бути змінені, наступний код буде помилковим:

void f()
{
    model = 200; // Ошибка: model является константой
    v[2] = 3;    // Ошибка: v[i] является константой
}

Зверніть увагу, що const змінює тип об'єкта, а не вказівку на те, як повинна бути призначена змінна. const обмежує способи роботи з об'єктом.

void g(const X∗ p)
{
    // Не можем изменить p здесь
}

void h()
{
    X val;  // Но можем модифицировать значение здесь
    g(&val);
    // ...
}

При використанні покажчика використовуються два об'єкти: сам покажчик і об'єкт, на який вказує. Префіксне оголошення вказівника з const робить константним об'єкт, а не вказівник. Щоб оголосити як const вказівник, а не об'єкт, на який він вказує, необхідно помістити const після символу вказівника. Наприклад:

void f1(char∗ p)
{
    char s[] = "Gorm";

    const char∗ pc = s;        // указатель на константу
    pc[3] = 'g';               // Ошибка: значение объекта является константой
    pc = p;                    // OK

    char ∗const cp = s;        // константный указатель
    cp[3] = 'a';               // OK
    cp = p;                    // Ошибка: cp является константой

    const char ∗const cpc = s; // константный указатель на константный объект
    cpc[3] = 'a';              // Ошибка: объект является константой
    cpc = p;                   // Ошибка: указатель является константой
}

Розташування const щодо базового типу не є принциповим, оскільки не існує типу даних const*. Принциповим є положення const щодо символу *. Тому можливі такі записи:

char ∗const cp;  // const указатель на char
char const∗ pc;  // указать на const char
const char∗ pc2; // указатель на const char

Об'єкт, який є константою при доступі через один покажчик, може змінюватися при доступі іншими способами. Це особливо корисно для аргументів функції. Оголошуючи аргумент покажчика як const , функції забороняється змінювати об'єкт, який вказує. Наприклад:

const char∗ strchr(const char∗ p, char c);
char∗ strchr(char∗ p, char c);

Перша версія використовується для рядків, елементи яких не повинні бути змінені функцією та повертає вказівник на const, який не дозволяє змінювати результат. Друга версія використовується для змінних рядків.

Ви можете призначити адресу неконстантної змінної вказівнику на константу, тому що це не може завдати жодної шкоди. Однак адресу константи не можна призначити неконстантному покажчику, оскільки це дозволить змінити значення об'єкта. Наприклад:

void f4()
{
    int a = 1;
    const int c = 2;
    const int∗ p1 = &c; // OK
    const int∗ p2 = &a; // OK
    int∗ p3 = &c;       // Ошибка: инициализация int* с const int*
    ∗p3 = 7;            // Попытка изменить значение c
}

constexpr

Константний вираз є виразом, який обчислюється під час компіляції. Константні вирази не можуть використовувати значення та змінні, які не відомі під час компіляції.

Існує безліч причин, з яких комусь може знадобитися іменована константа, а не буква або значення, що зберігається у змінній:

  1. Іменовані константи спрощують розуміння та підтримку коду.
  2. Змінна може бути змінена (тому ми маємо бути більш обережними у наших міркуваннях, ніж для константи).
  3. Мова вимагає постійних виразів для розмірів масивів, позначок case та аргументів значень шаблону.
  4. Програмісти вбудованих систем люблять поміщати незмінні дані в постійне запам'ятовуючий пристрій. Тому що доступна тільки для читання пам'ять дешевша, ніж динамічна пам'ять (з точки зору витрат та споживання енергії) і часто більш численна. Крім того, дані у постійній пам'яті захищені від більшості збоїв системи.
  5. Якщо ініціалізація виконується під час компіляції, то багатопоточної програмі системі може бути ніяких розбіжностей даних.
  6. Виконання обчислень на етапі компіляції покращує продуктивність програми.

Значення constexpr обчислюється під час виконання компіляції, і якщо воно не може бути обчислене, компілятор видасть помилку.

int x1 = 7;
constexpr int x2 = 7;
constexpr int x3 = x1;     // Ошибка: инициализатор не является константным выражением
constexpr int x4 = x2;     // OK

void f()
{
    constexpr int y3 = x1; // Ошибка: инициализатор не является константным выражением
    constexpr int y4 = x2; // OK
    // ...
}

Можливості константних виразів досить великі, оскільки є можливість використовувати цілі типи даних, дані з плаваючою точкою, перерахування, а також оператори, які не змінюють значення змінних (такі як +, ? і [] , але не = або ++ )

constexpr int isqrt_helper(int sq, int d, int a)
{
    return sq <= a ? isqrt_helper(sq+d,d+2,a) : d;
}

constexpr int isqrt(int x)
{
    return isqrt_helper(1,3,x)/2 − 1;
}

constexpr int s1 = isqrt(9);
constexpr int s2 = isqrt(1234);
Рекомендуємо хостинг TIMEWEB
Рекомендуємо хостинг TIMEWEB
Стабільний хостинг, на якому розміщується соціальна мережа EVILEG. Для проектів на Django радимо VDS хостинг.

Вам це подобається? Поділіться в соціальних мережах!

BlinCT
  • 05 червня 2017 р. 04:55

Отличное описание и примеры!

Коментарі

Only authorized users can post comments.
Please, Log in or Sign up
d
  • dsfs
  • 26 квітня 2024 р. 14:56

C++ - Тест 004. Указатели, Массивы и Циклы

  • Результат:80бали,
  • Рейтинг балів4
d
  • dsfs
  • 26 квітня 2024 р. 14:45

C++ - Тест 002. Константы

  • Результат:50бали,
  • Рейтинг балів-4
d
  • dsfs
  • 26 квітня 2024 р. 14:35

C++ - Тест 001. Первая программа и типы данных

  • Результат:73бали,
  • Рейтинг балів1
Останні коментарі
k
kmssr09 лютого 2024 р. 05:43
Qt Linux - Урок 001. Автозапуск програми Qt під Linux как сделать автозапуск для флэтпака, который не даёт создавать файлы в ~/.config - вот это вопрос ))
АК
Анатолий Кононенко05 лютого 2024 р. 12:50
Qt WinAPI - Урок 007. Робота з ICMP Ping в Qt Без строки #include <QRegularExpressionValidator> в заголовочном файле не работает валидатор.
EVA
EVA25 грудня 2023 р. 21:30
Boost - статичне зв&#39;язування в проекті CMake під Windows Ошибка LNK1104 часто возникает, когда компоновщик не может найти или открыть файл библиотеки. В вашем случае, это файл libboost_locale-vc142-mt-gd-x64-1_74.lib из библиотеки Boost для C+…
J
JonnyJo25 грудня 2023 р. 19:38
Boost - статичне зв&#39;язування в проекті CMake під Windows Сделал всё по-как у вас, но выдаёт ошибку [build] LINK : fatal error LNK1104: не удается открыть файл "libboost_locale-vc142-mt-gd-x64-1_74.lib" Хоть убей, не могу понять в чём дел…
G
Gvozdik19 грудня 2023 р. 08:01
Qt/C++ - Урок 056. Підключення бібліотеки Boost в Qt для компіляторів MinGW і MSVC Для решения твой проблемы добавь в файл .pro строчку "LIBS += -lws2_32" она решит проблему , лично мне помогло.
Тепер обговоріть на форумі
Evgenii Legotckoi
Evgenii Legotckoi03 травня 2024 р. 00:07
Мобильное приложение на C++Qt и бэкенд к нему на Django Rest Framework Добрый день. По моему мнению - да, но то, что будет касаться вызовов к функционалу Андроида, может создать огромные трудности.
IscanderChe
IscanderChe30 квітня 2024 р. 14:22
Во Flask рендер шаблона не передаётся в браузер Доброе утро! Имеется вот такой шаблон: <!doctype html><html> <head> <title>{{ title }}</title> <link rel="stylesheet" href="{{ url_…
G
Gar22 квітня 2024 р. 15:46
Clipboard Как скопировать окно целиком в clipb?
Павел Дорофеев
Павел Дорофеев14 квітня 2024 р. 12:35
QTableWidget с 2 заголовками Вот тут есть кастомный QTableView с многорядностью проект поддерживается, обращайтесь
f
fastrex04 квітня 2024 р. 14:47
Вернуть старое поведение QComboBox, не менять индекс при resetModel Добрый день! У нас много проектов в которых используется QComboBox, в версии 5.5.1, когда модель испускает сигнал resetModel, currentIndex не менялся. В версии 5.15 при resetModel происходит try…

Слідкуйте за нами в соціальних мережах