Статичні змінні у функціях членів


158

Чи можете мені хтось пояснити, як статичні змінні у функціях членів працюють у C ++.

Дано наступний клас:

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

Якщо я декларую кілька примірників A, чи викликає foo()на один екземпляр приріст статичної змінної iу всіх екземплярах? Або лише того, на кого його покликали?

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

Відповіді:


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

3
Дякую за гарний приклад! Чи існував би спосіб реально досягти чогось, що робить сферу static int iспецифічною для екземпляра, так що, наприклад, o1.foo(); // i = 1і $o2.foo(); // i = 1...?
Стінгері

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;
}

41
+1 для keyword static unfortunately has a few different unrelated meanings in C++:)
iammilind

світ має набагато більше сенсу після прочитання цього, ДЯКУЙТЕ
Ерін

Мені подобається трюк із шаблонами. Я не можу дочекатися, коли знайду привід використовувати його.
Томаш Зато - Відновити Моніку

Хтось отримав посилання на "дещо зневірений на користь неназваних просторів імен"?
austinmarton

3
@austinmarton: Фраза "Використання статики для позначення" локальної одиниці перекладу "застаріла в C ++. Замість цього використовувати неіменовані простори імен (8.2.5.1)" присутня на мові програмування C ++ у моєму виданні (10-го друку, вересень 1999 р.) на сторінці 819.
6502

2

Статичні змінні всередині функцій

  • Статична змінна створюється всередині функції, яка зберігається в статичній пам'яті програми, а не в стеці.

  • Ініціалізація статичної змінної буде здійснена під час першого виклику функції.

  • Статична змінна збереже значення у кількох функціональних викликах

  • Термін служби статичної змінної - Program

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

Приклади

#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функцією або функцією.


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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.