Лекция 4

Препроцессор

Основные директивы препроцессора

Условная компиляция

#define FLAG1

#ifdef FLAG1
  // Код, помещённый здесь, откомпилируется только,
  // если определена макроподстановка FLAG1
#else
  // Данный код откомпилируется, если макроподстановка FLAG1 не определена
#endif /* FLAG1 */ 

Стражи включения (include guards)

Include guards — шаблонная конструкция (клише), вставляемая в заголовочный файл.

#ifndef H_FILE_NAME_HPP
#define H_FILE_NAME_HPP

// Содержимое заголовочного файла

#enfif /* H_FILE_NAME_HPP */

Такая кострукция защищает проект от повторного включения прототипов функции и зацикливания на этапе препроцессирования, что, в свою очередь, приводит к сокращению времени компиляции, а в случае зацикливания к корректной сборке проекта.

Precompiled headers

В режиме включенных предкомпилированных заголовков при компиляции заголовочных файлов все они попадают в откомпилированном виде в файл *.pch (для Visual Studio), тогда при необходимости повторного использования они не компилируются заново, а берутся из *.pch. Это приводит к уменьшению времени компиляции в больших проектах.

Перечисления

Перечисления в C++98

Перечисления, согласно стандарту C++98, объявляются следующим образом: cpp enum DayOfWeek {MON, TUE, WED, THU, FRI, SAT, SUN};

Каждый элемент перечисления это целое число: MON = 0, TUE = 1, … При этом существует возможность явно задавать значения элементов enum. Например:

enum DayOfWeek {MON, TUE = 3, WED, THU, FRI, SAT, SUN};

Тогда MON = 0, TUE = 3, WED = 4, …

Благодаря тому, что все имена перечисления являются целыми числами возможно следующее присваивание:

int day = MON;     // Ok, day == 0;

DayOfWeek dow = 5  // Ошибка компиляции
DayOfWeek wod = static_cast<DayOfWeek>(5);

Перечисления в C++11

При объявлении enum все имена перечисления экспортируются во внешнюю обасть видимости, это приводит к проблеме коллизии имен в крупных проектах. Для решения данной проблемы в C++11 был введен enum class — строго типизированные перечисления с ограниченной областью видимости. Объявление enum class происходит следующим образом:

enum class DayOfWeek {MON, TUE, WED, THU, FRI, SAT = 6, SUN};

При этом особенности работы с ним следующие:

// Ошибка при преобразовании DayOfWeek к int
int day = DayOfWeek::WED;

// Ошибка: WED нет в облати видимости
int wday = WED;

// Присваение переменной значения из множества имен перечисления DayOfWeek
DayOfWeek dow = DayOfWeek::FRI;

// При необходимости присваивания переменной типа DayOfWeek целого числа
// можно воспользоваться оператором static_cast<type>(object);
DayOfWeek sat = static_cast<DayOfWeek>(6);

Использование перечислений

Часто имена перечислений используются в операторе switch.

switch (dayOfWeek) {
    case DayOfWeek::MON: cout << "Monday\n"; break;
    case DayOfWeek::TUE: cout << "Tuesday\n"; break;
    case DayOfWeek::WED: cout << "Wedesday\n"; break;
    case DayOfWeek::THU: cout << "Thusday\n"; break;
    case DayOfWeek::FRI: cout << "Friday\n"; break;
    case DayOfWeek::SAT: cout << "Satuday\n"; break;
    case DayOfWeek::SUN: cout << "Sunday\n"; break;
}

Массивы

Особенности массивов в С/C++:

Работа с массивами C/C++:

// Создание массива из 5 элементов типа int
int b[5]; 

// Создание и инициализация массива из 4 элементов 
int a[] = {2, 3, 5, 7};

// Запись в последний элемент масива
a[3] = 1;

// Так нельзя копировать, только в цикле
b = a;  

Так как массивы в C/C++ не помнят своего размера, то его необходимо определять вручную след образом:

int size = sizeof(arr2) / sizeof(int);
    // sizeof(arr2) возвращает размер массива в байтах
    // sizeof(int) возвращает размер элемента массива

Цикл по массиву (foreach)

int a[] = {3, 7, 9, 5, 7, 2, 7};

for (int x : a)     // Доступ к x только на чтение
    cout << x;  

for (int &x : a)    // x передается по ссылке, поэтому
    x += 1;         // доступ есть на чтение и запись

Передача массива в функцию

В C/C++ массив всегда передается по ссылке, поэтому использование оператора sizeof(), для определения его размера, бесполезно. Потому при передаче массива в функцию следует явно передавать и его размер.

const int n = 5;
int a[n] = {1, 3, 5, 6, 1};  // создали массив фиксированной длины 5

// …

void f(int a[], int len) {   // создали функцию для работы с массивами
  // …                       // любой заданной длины len
}

// …

f(a, n);                     // вызвали функцию для нашего массива длины 5
  1. На главную