static local variables
В этом давнишнем посте кратко резюмировали все стороны "употребления" ключевого слова static. Сегодня поговорим про статические локальные переменные.
Это довольно интересная сущность, которая сочетает в себе поведение локального объекта функции и глобальной переменной.
От локального объекта она берет область видимости. То есть к этой переменной по имени никак нельзя обратиться вне ее функции. Можно, например, вернуть из функции ссылку на эту переменную и иметь возможность ее читать и модифицировать. Но по имени к ней можно обратиться только внутри функции. Соответственно, у static local variable нет никакого собственного типа линковки, это бессмысленно.
От глобальной переменной она берет статическое время жизни. То есть, начиная с момента своей инициализации, она продолжает существовать, пока не вызовется std::exit aka завершение программы.
Разберем немного цикл жизни такой переменной.
1) Она инициализируется при первом достижении исполнения ее объявления. Стандарт нам говорит:
То есть нам дается очень важная гарантия: локальные статические переменные инициализируются потокобезопасно. Это значит, что даже если несколько потоков одновременно зайдут в функцию и попытаются проинициализировать переменную, то победителем в этой истории будет только один поток, который и проведет инициализацию, все остальные будут ждать. Эта гарантия появляется вместе с появлением новой модели памяти и исполнения в С++11. И обычно реализуется с помощью паттерна блокировки с двойной проверкой.
Однако, если переменная числового типа или инициализируется с помощью константного выражения, то инициализация может произойти раньше(какой смысл ждать, если все понятно как делать и делать это просто).
2) При выходе из скоупа функции для статической локальной переменной не вызывается деструктор. Она продолжает жить не тужить и сохраняет свое значение до следующего вызова функции.
3) При повторном заходе в функцию объявление переменной просто игнорируется и выполняется весь код, помимо инициализации. Здесь мы можем повторно использовать переменную, изменить ее значение и вообще много чего с ней делать.
4) После завершения функции main переменная разрушается. Press F умершим.
Пример:
Функция BytesToHex переводит любое количество байт от заданного указателя в их hex представление. Раз мы знаем, что hex представление содержит только 16 символов и больше нигде эти символы не нужны, то очень удобно поместить массив этих символов в саму функцию в качестве локальной статической переменной. Так мы инкапсулируем данные и сохраним возможность 1 раз создать переменную и пользоваться именно этим инстансом во всех вызовах функции.
Один интересный момент, что kHexDigits инициализируется не при первом вызове функции. Потому что в первый раз исполнение не прошло через ее декларацию. И только начиная со второго вызова она начинает существовать и разрушается только после выхода из main().
Combine your best sides. Stay cool.
#cpp11 #multitasking #cppcore
В этом давнишнем посте кратко резюмировали все стороны "употребления" ключевого слова static. Сегодня поговорим про статические локальные переменные.
Это довольно интересная сущность, которая сочетает в себе поведение локального объекта функции и глобальной переменной.
От локального объекта она берет область видимости. То есть к этой переменной по имени никак нельзя обратиться вне ее функции. Можно, например, вернуть из функции ссылку на эту переменную и иметь возможность ее читать и модифицировать. Но по имени к ней можно обратиться только внутри функции. Соответственно, у static local variable нет никакого собственного типа линковки, это бессмысленно.
От глобальной переменной она берет статическое время жизни. То есть, начиная с момента своей инициализации, она продолжает существовать, пока не вызовется std::exit aka завершение программы.
Разберем немного цикл жизни такой переменной.
1) Она инициализируется при первом достижении исполнения ее объявления. Стандарт нам говорит:
such a variable is initialized the first
time control passes through its declaration; [...]
If control enters the declaration concurrently
while the variable is being initialized,
the concurrent execution shall wait for
completion of the initialization.
То есть нам дается очень важная гарантия: локальные статические переменные инициализируются потокобезопасно. Это значит, что даже если несколько потоков одновременно зайдут в функцию и попытаются проинициализировать переменную, то победителем в этой истории будет только один поток, который и проведет инициализацию, все остальные будут ждать. Эта гарантия появляется вместе с появлением новой модели памяти и исполнения в С++11. И обычно реализуется с помощью паттерна блокировки с двойной проверкой.
Однако, если переменная числового типа или инициализируется с помощью константного выражения, то инициализация может произойти раньше(какой смысл ждать, если все понятно как делать и делать это просто).
2) При выходе из скоупа функции для статической локальной переменной не вызывается деструктор. Она продолжает жить не тужить и сохраняет свое значение до следующего вызова функции.
3) При повторном заходе в функцию объявление переменной просто игнорируется и выполняется весь код, помимо инициализации. Здесь мы можем повторно использовать переменную, изменить ее значение и вообще много чего с ней делать.
4) После завершения функции main переменная разрушается. Press F умершим.
Пример:
std::string BytesToHex(const void* bytes, size_t size)
{
if (size) {
static const char kHexDigits[] = {'0', '1', '2', '3', '4', '5', '6', '7',
'8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
std::string output;
output.reserve(size * 2);
auto c = static_cast<const uint8_t*>(bytes);
for (size_t i = 0; i < size; ++i) {
uint8_t value = *(c + i);
output.push_back(kHexDigits[value >> 4]);
output.push_back(kHexDigits[value & 0xf]);
}
return output;
}
else {
return "";
}
}
int main()
{
std::cout << BytesToHex("", 0) << std::endl;
std::cout << BytesToHex("123", 3) << std::endl;
std::cout << BytesToHex("abc", 3) << std::endl;
}
Функция BytesToHex переводит любое количество байт от заданного указателя в их hex представление. Раз мы знаем, что hex представление содержит только 16 символов и больше нигде эти символы не нужны, то очень удобно поместить массив этих символов в саму функцию в качестве локальной статической переменной. Так мы инкапсулируем данные и сохраним возможность 1 раз создать переменную и пользоваться именно этим инстансом во всех вызовах функции.
Один интересный момент, что kHexDigits инициализируется не при первом вызове функции. Потому что в первый раз исполнение не прошло через ее декларацию. И только начиная со второго вызова она начинает существовать и разрушается только после выхода из main().
Combine your best sides. Stay cool.
#cpp11 #multitasking #cppcore