﻿# Storage duration

Storage duration – это свойство идентификатора, определяющее правила, в соответствии с которыми объект будет создан и разрушен\. Существуют 4 вида storage duration: _automatic_, _static_, _thread local_ и _dynamic_\.

Storage duration тесно связан со временем жизни объекта\. Например, два глобальных объекта со свойством _static storage duration_ имеют одинаковое время жизни – все время выполнения программы\. В то же время два объекта с _dynamic storage duration_ будут иметь разное время жизни\. Оно зависит от того, когда вызваны соответствующие функции для [управления динамической памятью](https://en.cppreference.com/w/cpp/memory)\.

## Automatic storage duration

Объекты, имеющие _automatic storage duration_, создаются в начале обрамляющего блока кода и разрушаются в его конце\. Такими объектами являются локальные объекты, объявленные без спецификатора _static, extern _или _thread\_local_\.

Рассмотрим следующий синтетический пример:

```cpp
#include <vector>
#include <string>

class Forecaster { .... };

float Convert(const std::string &temp);

std::vector<float>
PredictTemperatureForInterval(int first_day,
                              int last_day,
                              const Forecaster &forecaster)
{
  auto forecast = forecaster.Predict(GetTodayDate());
  if (first_day == last_day)
  {
    std::string single_temperature = forecast.GetTemperature(first_day);
    if (!Validate(single_temperature))
    {
      return {};
    }

    return { Convert(single_temperature) };
  }
  else
  {
    std::vector<float> multiple_temperatures;
    for (auto curr_day = first_day; cur_day < last_day; ++cur_day)
    {
      std::string curr_temperature = forecast.GetTemperature(curr_day);
      if (!Validate(curr_temperature)
      {
        return {};
      }
      multiple_temperatures.push_back(Convert(cur_temperature));
    }

    return multiple_temperatures;
  }
}
```

В данном примере переменные _forecast_, _single\_temperature_, _multiple\_temperatures_ и _cur\_day_ имеют _automatic storage duration\._ Однако время жизни для каждой из переменных разное\.

Время жизни переменной _forecast_ – тело функции _PredictTemperatureForInterval;_ _single\_temperature_ – _than_\-ветка оператора _if_; _multiple\_temperatures_ – _else_\-ветка оператора _if_; a _cur\_day_ – тело цикла _for_\.

## Static storage duration

Для объектов, имеющих _static storage duration_, память выделяется в начале исполнения программы и освобождается при её завершении\. При этом сам объект создаётся перед первым обращением к нему\. Объектами со _static storage duration_ являются все идентификаторы, объявленные в некотором пространстве имён, а также идентификаторы, объявленные спецификатором _static_ или _extern_\. Для каждого имени, имеющего _static storage duration_, создается ровно один экземпляр\.

Рассмотрим пример:

```cpp
class Logger { .... };

static Logger Logger;

int Calc(int arg);

int CalcWithLogging (int arg)
{
  static int counter = 0;
  ++counter;
  Logger.Log(counter);
  return Calc(arg);
}
```

В данном фрагменте кода переменные _logger_ и _counter_ имеют _static storage duration_\. Для обеих переменных будет выделена память в начале выполнения программы\. Переменная _logger_ является глобальной переменной\. Её инициализация – вызов конструктора по умолчанию класса _Logger_ – произойдёт перед началом выполнения функции _main_\. Переменная _counter_ является локальной переменной функции _CalcWithLogging_\. Её инициализация произойдёт при первом вызове этой функции\. Если во время выполнения программы функция _CalcWithLogging_ не будет вызвана, то и переменная _counter_ инициализирована не будет\. В то же время память для _counter_ будет выделена и освобождена соответствующим образом\.

## Thread storage duration

Память для объекта, имеющего _thread storage duration_, выделяется при инициализации потока исполнения и освобождается при его завершении\. Сам объект создается перед первым обращением к нему\. Для каждого потока создаётся отдельный экземпляр\. Чтобы идентификатор имел _thread storage duration_, его нужно объявить, используя спецификатор _thread\_local_\. Объявление объекта, имеющего _thread storage duration_, может также содержать спецификаторы _static_ или_ extern_\. В таком случае эти спецификаторы не влияют на _storage duration_, а определяют его [_linkage_](https://pvs-studio.ru/ru/blog/terms/6506/)\.

Рассмотрим следующий синтетический пример:

```cpp
#include <string>
#include <string_view>
#include <iostream>
#include <syncstream>
#include <thread>

thread_local std::string str;

void AppendSuffix(std::string_view suffix)
{
  str += suffix;
}

void ThreadFunc(const std::string &value)
{
  AppendSuffix(value);
  std::osyncstream { std::cout } << str;
}

int main()
{
  AppendSuffix("main");
  std::thread t1 { ThreadFunc, "thread 1 " };
  std::thread t2 { ThreadFunc, "thread 2 " };
  t1.join();
  t2.join();
  std::cout << str;
}
```

В этом примере переменная _str_ имеет _thread storage duration_\. При выполнении программы для каждого из потоков _t1_ и _t2_ будет создана своя копия переменной _str\._ Поэтому программа выведет одно из двух: "thread 1 thread 2 main", "thread 2 thread 1 main" – в зависимости от порядка выполнения работы потоков\.

## Dynamic storage duration

Чтобы создать или уничтожить объект с _dynamic storage duration_, нужно использовать специальные функции для [управления динамической памятью](https://en.cppreference.com/w/cpp/memory)\. Для создания такого объекта можно, например, использовать оператор [_new_](https://en.cppreference.com/w/cpp/memory/new/operator_new)\. Тогда этот объект будет существовать до тех пор, пока не будет вызван соответствующий оператор [_delete_](https://en.cppreference.com/w/cpp/memory/new/operator_delete)\. Рассмотрим следующий пример кода:

```cpp
void Foo()
{
  int *pInt = new int;
  *pInt = 12;
  cout << *pInt << '\n';
  delete pInt;
}
```

Здесь переменная _pInt_ имеет _dynamic storage duration_\. Память для неё выделяется при выполнении оператора _new_, а освобождается при выполнении оператора _delete_\.