Статические переменные в функциях-членах

158

Может кто-нибудь объяснить, как статические переменные в функциях-членах работают в C ++.

Учитывая следующий класс:

class A {
   void foo() {
      static int i;
      i++;
   }
}

Если я объявляю несколько экземпляров A, foo()увеличивает ли вызов одного экземпляра статическую переменную iво всех экземплярах? Или только тот, который был вызван?

Я предполагал, что у каждого экземпляра будет своя собственная копия i, но пошаговое выполнение некоторого кода, который я имею, указывает на обратное.

monofonik
источник

Ответы:

169

Так class Aкак это не шаблонный класс и A::foo()не шаблонная функция. Внутри программы будет только одна копия static int i.

Любой экземпляр Aобъекта будет влиять на то же самое, iи время жизни iостанется в программе. Чтобы добавить пример:

A o1, o2, o3;
o1.foo(); // i = 1
o2.foo(); // i = 2
o3.foo(); // i = 3
o1.foo(); // i = 4
iammilind
источник
3
Спасибо за хороший пример! Был бы способ на самом деле достичь чего-то, что делает область действия static int iспецифичной для данного экземпляра, например, o1.foo(); // i = 1и $o2.foo(); // i = 1...?
Stingery
14
Хотя это может быть не тот стиль, который вы ищете, если вы сделаете, например, член с частными данными класса A, то эффект, который вы описываете. Если вас беспокоит конфликт имен, вы можете добавить префикс, например, m_для обозначения статуса i.
Карл Моррис
137

К staticсожалению, это ключевое слово имеет несколько разных значений в C ++.

  1. При использовании для элементов данных это означает, что данные размещаются в классе, а не в экземплярах.

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

  3. При использовании на уровне модуля компиляции (модуля) это означает, что переменная похожа на глобальную (то есть размещена и инициализирована до mainзапуска и уничтожена после mainвыхода), но переменная не будет доступна или видима в других модулях компиляции .

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

В вашем коде staticключевое слово используется со значением номер 2 и не имеет ничего общего с классами или экземплярами ... это переменная функции, и будет только одна ее копия.

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

#include <stdio.h>

template<int num>
void bar()
{
    static int baz;
    printf("bar<%i>::baz = %i\n", num, baz++);
}

int main()
{
    bar<1>(); // Output will be 0
    bar<2>(); // Output will be 0
    bar<3>(); // Output will be 0
    bar<1>(); // Output will be 1
    bar<2>(); // Output will be 1
    bar<3>(); // Output will be 1
    bar<1>(); // Output will be 2
    bar<2>(); // Output will be 2
    bar<3>(); // Output will be 2
    return 0;
}
6502
источник
41
+1 для keyword static unfortunately has a few different unrelated meanings in C++:)
iammilind
после прочтения этого мира стало гораздо больше смысла, СПАСИБО
Эрин
Мне нравится трюк с шаблонами. Я не могу дождаться, чтобы найти оправдание, чтобы использовать его.
Томаш Зато - Восстановить Монику
Кто-нибудь получил ссылку на «несколько обескураженный в пользу безымянных пространств имен»?
Остинмартон
3
@austinmarton: фраза «Использование static для обозначения« local to translation unit »устарела в C ++. Вместо этого используйте безымянные пространства имен (8.2.5.1)», присутствующая на языке программирования C ++ в моем издании (10-й выпуск, сентябрь 1999 г.) на странице 819.
6502
2

Статические переменные внутри функций

  • Статическая переменная создается внутри функции и хранится в статической памяти программы, а не в стеке.

  • Инициализация статической переменной будет производиться при первом вызове функции.

  • Статическая переменная сохранит значение в нескольких вызовах функций

  • Время жизни статической переменной - Программа

введите описание изображения здесь

Примеры

#include <iostream>

using namespace std;

class CVariableTesting 
{
    public:
    
    void FuncWithStaticVariable();
    void FuncWithAutoVariable();

};

void CVariableTesting::FuncWithStaticVariable()
{
    static int staticVar = 0; //staticVar is initialised by 0 the first time
    cout<<"Variable Value : "<<staticVar<<endl;
    staticVar++;
}
void CVariableTesting::FuncWithAutoVariable()
{
    int autoVar = 0;
    cout<<"Variable Value : "<<autoVar<<endl;
    autoVar++;
}
    

int main()
{
    CVariableTesting objCVariableTesting;
    cout<<"Static Variable";
    objCVariableTesting.FuncWithStaticVariable();
    objCVariableTesting.FuncWithStaticVariable();
    objCVariableTesting.FuncWithStaticVariable();
    objCVariableTesting.FuncWithStaticVariable();
    objCVariableTesting.FuncWithStaticVariable();
    
    cout<<endl;
    cout<<"Auto Variable";
    objCVariableTesting.FuncWithAutoVariable();
    objCVariableTesting.FuncWithAutoVariable();
    objCVariableTesting.FuncWithAutoVariable();
    objCVariableTesting.FuncWithAutoVariable();
    objCVariableTesting.FuncWithAutoVariable();
    
    return 0;
}

Вывод :

Статическая переменная

Значение переменной: 0
Значение переменной: 1
Значение переменной: 2
Значение переменной: 3
Значение переменной: 4

Авто Переменная

Значение переменной: 0
Значение переменной: 0
Значение переменной: 0
Значение переменной: 0
Значение переменной: 0

Саураб Раот
источник
-2

Упрощенный ответ:

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

0xbadf00d
источник
9
Нет. Глобальные переменные инициализируются при запуске программы, статические функции инициализируются при первом использовании. Это большая разница.
6502
Я не думаю, что это то, что происходит. Тем не менее, в любом случае это должно зависеть от компилятора.
0xbadf00d
2
Тогда вы ошибаетесь: 3.6.1 в стандарте C ++ диктует, что при запуске происходит создание объекта области пространства имен со статической продолжительностью хранения; 6.7 (4) диктует, что в целом «... такая переменная инициализируется при первом прохождении контроля через ее объявление; такая переменная считается инициализированной после завершения ее инициализации». Кстати, эта инициализация при первом использовании очень удобна для реализации ленивых одноэлементных конструкций.
6502
3.7.4: «Постоянная инициализация (3.6.2) объекта области блока со статической продолжительностью хранения, если это применимо, выполняется до того, как его блок впервые вводится. Реализация может выполнять раннюю инициализацию других переменных области блока с длительность хранения статических или потоков при тех же условиях, в которых реализации разрешается статически инициализировать переменную с продолжительностью хранения статических или потоков в области имен (3.6.2). В противном случае такая переменная инициализируется при первом прохождении контроля через ее объявление; "
0xbadf00d
1
Любопытно, что: 1) для постоянной инициализации неуместно обсуждать, можно ли инициализировать локальную статику перед первым входом в блок (переменная видна только внутри блока, а постоянная инициализация не вызывает побочных эффектов); 2) ничего в вашем посте не сказано о постоянной инициализации; 3) локальная статика очень полезна для непостоянной инициализации, как, например, MyClass& instance(){ static MyClass x("config.ini"); return x; }допустимая переносимая реализация для однопоточного использования именно потому, что локальная статика НЕ ​​просто похожа на глобальную, несмотря на то, что вы говорите.
6502