Skip to the content.

Глобальные переменные

Объявление глобальной переменной (в заголовочном файле): extern int global;

Определение глобальной переменной (в любой единице трансляции): int global = 10;

У глобальных переменных достаточно проблем и их использования стоит избегать.

Ключевое слово static

Static имеет пять различных применений.

Статические глобальные переменные

Статическая глобальная переменная - глобальная переменная, доступная только в пределах модуля.

static int global = 10;

Статические локальные переменные

Статическая локальная переменная - глобальная переменная, доступная только в пределах функции (в которой определена).

По сути мы увеличиваем время жизни локальной переменной, её время жизни: от первого вызова функции и до конца программы.

Такая переменная будет храниться не на стеке, а в той же области где храняться другие глобальные переменные (.bss, .data).

int next(int start = 0)
{
    static int k = start;
    return k++;
}
next(10); // k инициализируется числом 10
next(20); // ничего не произойдёт, k уже инициализирована

Статические функции

Статическая функция - функция, доступная только в пределах модуля.

Две разные функции, но с одинаковыми сигнатурами:

// 1.cpp
static void test()
{
    cout << "A\n";
}
// 2.cpp
static void test()
{
    cout << "B\n";
}

Подстановка адресов этих функций происходит локально - только в модулях где они определены, поэтому не происходит коллизии. Говорят, что статические глобальные переменные и стаические функции проходят внутренюю линковку. Обычные функции и глобальные переменные проходят внешнюю линковку.

Отсюда и взялось слово extern (при объявлении глобальных переменных), его можно использовать и для объявления функций, однако определение и объявление функции и так очень сильно синтаксически отличаются, поэтому его всегда опускают.

Статические поля класса

Статические поля класса - глобальные переменные, определённые внутри класса.

Объявление:

struct User
{
...
private:
    static size_t instances_;
};

Определение:

size_t User::instances_ = 0;

Модификаторы доступа это единственное что отличает статические поля класса от глобальных переменных.

Статические поля класса храняться в той же области памяти, где храняться глобальные переменные.

Обычные поля класса храняться в экземпляре, а статические поля класса храняться в области глобальных переменных, грубо говоря принадлежат самому классу и изменяются одновременно у всех экземпляров. Поэтому для доступа к статическим полям класса не нужен экземпляр (если бы модификатор был public: User::instances_)

Это может быть полезно, например, если нам нужно посчитать количество экземпляров класса, мы можем завести статическое поле класса, которое будем инкрементировать в конструкторе и декрементировать в деструкторе, при этом оно должно быть приватным, что бы нельзя было его накрутить.

Статические методы

Статические методы - методы имеющие доступ к закрытым полям и методам. Обычные методы вроде бы тоже имеют доступ к static полям, однако для обычных методов обязательно наличие экземпляра!

struct User
{
...
static size_t count() { return instances_; }
private:
    static size_t instances_;
};

Для вызова статического метода не нужен объект (у статического метода нет this):

cout << User::count();

Ключевое слово inline

inline говорит компилятору что функция достаточно проста и советует её встроить.

Встроить функцию означает заменить вызов функции на само тело функции.

Дешевле будет встроить следующую функцию чем вызвать (работа со стеком с кучей операций против одного умножения):

inline double square(double x) { return x * x; }

Однако inline накладывает некоторые ограничения:

А вообще современные компиляторы достаточно умные и могут сами решить встраивать функцию или нет даже если не указывать inline, так что inline по большей части используют для того, что бы сразу описать функцию в заголовочном файле и не разбивать на определение и объявление (таким образом можно делать библиотеки состоящие только из заголовочных файлов). Однако не все так красочно, мы получаем удобную конструкцию, однако платим временем компиляции, ведь заголовочный файл будет полностью вставляться в каждую единицу трансляции и каждую функцию из заголовочного файла приходиться компилировать по много раз!

Правило одного определения - One Definition Rule (ODR)