Почему следующая программа не печатает "Created 10 objects" ?
#include <iostream>
#include <cstddef>
struct A {
A() { counter++; }
~A() { counter--; }
static inline std::size_t counter = 0;
};
std::size_t objects_number = 10;
int main() {
label:
A a;
if (A::counter < objects_number)
goto label;
std::cout << "Created " << objects_number << " objects" << std::endl;
}
Открываем cppreference:
The goto statement transfers control to the location specified by label. The goto statement must be in the same function as the label it is referring, it may appear before or after the label.
If transfer of control exits the scope of any automatic variables (e.g. by jumping backwards to a point before the declarations of such variables or by jumping forward out of a compound statement where the variables are scoped), the destructors are called for all variables whose scope was exited, in the order opposite to the order of their construction.
То есть если с помощью goto выйти из области существования (scope) какой-либо автоматической переменной, то для этой переменной вызывается деструктор.
В том числе это происходит если выполнить прыжок назад, на строчку, которая находится выше, чем объявление переменной.
Ваш пример - очередная демонстрация различий между концепциями времени жизни (lifetime) и продолжительности хранения (storage duration).
Ваш класс A
обладает нетривиальным конструктором и деструктором. Это означает, что каждая итерация вашего goto-цикла прекращает время жизни вашего объекта a
вызовом деструктора (как описано в цитате в ответе @HolyBlackCat) и затем снова начинает его время жизни вызовом конструктора. В каждый момент времени существует только один объект a
, что и иллюстрируется значением вашего счетчика. При этом все эти объекты гарантированно занимают одно и то же положение в памяти - продолжительность хранения a
все это время не прекращается. Она прекратится только при выходе из блока.
Доступ к a
после вызова его деструктора приведет к неопределенному поведению. Используя менее тривиальный тип для демонстрации
std::string *ptr = nullptr;
back:
if (ptr != nullptr)
std::cout << *ptr << std::endl; // <- Неопределенное поведение
std::string a = "Hello World";
if (ptr == nullptr)
{
ptr = &a;
goto back;
}
Но стоит вам избавиться от нетривиального деструктора, как время жизни объекта уже не будет прекращаться в момент обратного goto. Время жизни будет продолжаться вплоть до прекращения его продолжительности хранения (или до переинициализации)
// В предположении, что `std::array<int, 3>` является тривиальным типом
std::array<int, 3> *ptr = nullptr;
back:
if (ptr != nullptr)
std::cout << (*ptr)[1] << std::endl; // <- Поведение определено: выводит 2
std::array<int, 3> a = { 1, 2, 3 };
if (ptr == nullptr)
{
ptr = &a;
goto back;
}
Виртуальный выделенный сервер (VDS) становится отличным выбором
Пытаюсь вытащить X509Certificate из файла подписиsig чтобы эту подпись проверить
При создании проекта, просто ничего не происходитВ отладке netbeans вываливаются сообщения, но разобрать я их не могу