Допустим, я создаю:

class Hello {
    public:
        int World(int in)
        {
            static int var = 0;    // <<<< This thing here.
            if (in >= 0) {
                var = in;
            } else {
                cout << var << endl;
            }
        }
};

Теперь, если я это сделаю:

Hello A;
Hello B;

A.World(10);
A.World(-1);
B.World(-1);

Я получаю «10», за которым следует еще «10». Значение локальной переменной метода только что перешло от одного экземпляра класса к другому.

Это не удивительно - технически методы - это просто функции со скрытым параметром this, поэтому статическая локальная переменная должна вести себя так же, как в обычных функциях. Но это гарантировано ? Является ли это поведением, предписываемым стандартом, или это просто счастливый побочный продукт того, как компилятор обрабатывает методы? Другими словами - безопасно ли использовать такое поведение? (... сверх стандартного риска сбить с толку непривычного ...)

0
SF. 6 Май 2016 в 17:44

4 ответа

Лучший ответ

Да. Не имеет значения, является ли функция [нестатическим] членом класса или нет, гарантируется наличие только одного экземпляра его статических переменных.

Правильное техническое объяснение таких переменных состоит в том, что это объекты с static duration и internal linkage - и, таким образом, эти имена действуют до выхода из программы, и все экземпляры этого имени относятся к одному и тому же объекту.

3
NathanOliver 6 Май 2016 в 15:08

К правильному ответу можно добавить только одно. Если ваш класс был шаблонным, то экземпляр var будет совместно использоваться только объектами одного и того же типа экземпляра. Итак, если у вас было:

template<typename C>
class Hello {
    public:
        int World(int in)
        {
            static int var = 0;    // <<<< This thing here.
            if (in >= 0) {
                var = in;
            } else {
                cout << var << endl;
            }
        }
};

А потом:

Hello<int> A;
Hello<int> B;
Hello<unsigned> C;

A.World(10);
A.World(-1);
B.World(-1);
C.World(-1);

Тогда окончательный результат будет «0», а не «10», потому что экземпляр Hello<unsigned> будет иметь свою собственную копию var.

1
david 25 Июл 2018 в 08:54

Если мы говорим о компиляторе Windows, это гарантировано

https://msdn.microsoft.com/en-us/library/y5f6w579.aspx

В следующем примере показана локальная переменная, объявленная статической в функции-члене. Статическая переменная доступна для всей программы; все экземпляры типа используют одну и ту же копию статической переменной.

Они используют пример, очень похожий на ваш.

Я не знаю о GCC

0
Kons 6 Май 2016 в 14:53

Да, это гарантировано. Теперь, чтобы ответить на вопрос "Есть ли риск совместного использования локальной статической переменной метода между экземплярами?" это могло быть немного менее просто. Могут существовать потенциальные риски при инициализации и использовании переменной, и эти риски специфичны для переменных, локальных для метода (в отличие от переменных класса).

Для инициализации соответствующая часть в стандарте - 6.7 / 4 [stmt.dcl]:

Динамическая инициализация переменной области блока со статической продолжительностью хранения (3.7.1) или продолжительностью хранения потока (3.7.2) выполняется при первом прохождении управления через ее объявление; такая переменная считается инициализированной после завершения ее инициализации. Если инициализация завершается выдачей исключения, инициализация не завершена, поэтому она будет повторена, когда в следующий раз элемент управления войдет в объявление. Если элемент управления входит в объявление одновременно с инициализацией переменной, параллельное выполнение должно ждать завершения инициализации. Если элемент управления повторно входит в объявление рекурсивно во время инициализации переменной, поведение не определено.

В простых случаях все должно работать должным образом. Когда создание и инициализация переменной более сложны, в этом случае будут риски. Например, если конструктор бросает, у него будет возможность бросить снова при следующем вызове. Другой пример - рекурсивная инициализация, которая является неопределенным поведением.

Другой возможный риск - это эффективность метода. Компилятору потребуется реализовать механизм, обеспечивающий совместимую инициализацию переменной. Это зависит от реализации и вполне может быть блокировкой для проверки того, инициализирована ли переменная, и эта блокировка может выполняться каждый раз при вызове метода. Когда это происходит, это может существенно отрицательно сказаться на производительности.

0
Come Raczy 6 Май 2016 в 15:17