Перейти к содержанию

Последнее обновление:2026-10-13

C++ static: статические переменные и члены

Об авторе

Публичный аккаунт в WeChat:

C++ static подробно: статические переменные, члены и функции | C++那些事

При использовании с разными сущностями ключевое слово static имеет разный смысл. Мы можем использовать ключевое слово static:

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

Статические члены класса: объекты класса и функции в классе

Теперь рассмотрим эти способы применения static подробнее:

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

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

Когда переменная объявлена как static, память под неё выделяется на всё время жизни программы. Даже если функция вызывается многократно, память под статическую переменную выделяется лишь один раз, а значение переменной из предыдущего вызова передаётся в следующий вызов функции. Это очень полезно в C / C++ и в любых других приложениях, где нужно сохранять предыдущее состояние функции.

#include <iostream> 
#include <string> 
using namespace std; 

void demo() 
{ 
    // static variable 
    static int count = 0; 
    cout << count << " "; 

    // value is updated and 
    // will be carried to next 
    // function calls 
    count++; 
} 

int main() 
{ 
    for (int i=0; i<5; i++)  
        demo(); 
    return 0; 
} 

Вывод:

0 1 2 3 4 

В приведённой выше программе видно, что переменная count объявлена как static. Поэтому её значение передаётся от одного вызова функции к другому. При каждом вызове функции переменная count не инициализируется заново.

  • Статические переменные в классе

Поскольку переменные, объявленные static, инициализируются только один раз — им выделяется память в отдельном статическом хранилище, — статические переменные в классе разделяются объектами. Для разных объектов не может существовать нескольких копий одной и той же статической переменной. Именно по этой причине статические переменные нельзя инициализировать с помощью конструктора.

#include<iostream> 
using namespace std; 

class Apple 
{ 
public: 
    static int i; 

    Apple() 
    { 
        // Do nothing 
    }; 
}; 

int main() 
{ 
Apple obj1; 
Apple obj2; 
obj1.i =2; 
obj2.i = 3; 

// prints value of i 
cout << obj1.i<<" "<<obj2.i; 
} 

В приведённой выше программе видно, что мы попытались создать несколько копий статической переменной i для нескольких объектов. Но этого не произошло. Поэтому статическую переменную в классе пользователь должен явно инициализировать вне класса, используя имя класса и оператор разрешения области видимости, как показано ниже:

#include<iostream> 
using namespace std; 

class Apple 
{ 
public: 
    static int i; 

    Apple() 
    { 
        // Do nothing 
    }; 
}; 

int Apple::i = 1; 

int main() 
{ 
    Apple obj; 
    // prints value of i 
    cout << obj.i; 
} 

Вывод:

1

Статические члены

  • Объекты класса как статические

Как и переменные, объекты, объявленные static, тоже имеют область видимости, которая длится до конца времени жизни программы.

Рассмотрим следующую программу, в которой объект не является статическим.

#include<iostream> 
using namespace std; 

class Apple 
{ 
    int i; 
    public: 
        Apple() 
        { 
            i = 0; 
            cout << "Inside Constructor\n"; 
        } 
        ~Apple() 
        { 
            cout << "Inside Destructor\n"; 
        } 
}; 

int main() 
{ 
    int x = 0; 
    if (x==0) 
    { 
        Apple obj; 
    } 
    cout << "End of main\n"; 
} 

Вывод:

Inside Constructor
Inside Destructor
End of main

В приведённой выше программе объект объявлен нестатическим внутри блока if. Поэтому область видимости переменной ограничена блоком if. Следовательно, при создании объекта вызывается конструктор, а деструктор вызывается в тот момент, когда управление выходит за пределы блока if, потому что область видимости объекта ограничена блоком if, в котором он объявлен.

Если же объявить объект статическим, посмотрим, как изменится вывод.

#include<iostream> 
using namespace std; 

class Apple 
{ 
    int i; 
    public: 
        Apple() 
        { 
            i = 0; 
            cout << "Inside Constructor\n"; 
        } 
        ~Apple() 
        { 
            cout << "Inside Destructor\n"; 
        } 
}; 

int main() 
{ 
    int x = 0; 
    if (x==0) 
    { 
        static Apple obj; 
    } 
    cout << "End of main\n"; 
} 

Вывод:

Inside Constructor
End of main
Inside Destructor

Вы явно видите изменение вывода. Теперь деструктор вызывается после завершения main. Это происходит потому, что область видимости статического объекта распространяется на всё время жизни программы.

  • Статические функции в классе

Как и статические поля данных или статические переменные в классе, статические функции-члены не зависят от объектов класса. Нам разрешено вызывать статическую функцию-член через объект и '.', однако рекомендуется вызывать статические члены, используя имя класса и оператор разрешения области видимости.

Статическим функциям-членам разрешён доступ только к статическим полям данных или другим статическим функциям-членам; они не могут обращаться к нестатическим полям данных или функциям-членам класса.

#include<iostream> 
using namespace std; 

class Apple 
{ 
    public: 
        // static member function 
        static void printMsg() 
        {
            cout<<"Welcome to Apple!"; 
        }
}; 

// main function 
int main() 
{ 
    // invoking a static member function 
    Apple::printMsg(); 
} 

Вывод:

Welcome to Apple!

Ограничение области доступа

static также выполняет роль ограничения области доступа (аналогично анонимному пространству имён). Из issue: https://github.com/Light-City/CPlusPlusThings/issues/142

// source1.cpp
extern void sayHello();
const char* msg = "Hello World!\n";
int main()
{
    sayHello();
    return 0;
}

// source2.cpp
#include <cstdio>
extern char* msg;
void sayHello()
{
    printf("%s", msg);
}

g++ нормально компилирует два приведённых выше файла и печатает Hello World!, но если добавить static к msg в source1.cpp, возникнет ошибка компиляции 'undefined reference to msg':

// source1.cpp
extern void sayHello();
static const char* msg = "Hello World!\n";
int main()
{
    sayHello();
    return 0;
}

Внутреннее связывание static (ограничение области доступа)

Благодарим issue #142 за замечание: когда static квалифицирует глобальную переменную или функцию, он также выполняет роль ограничения области доступа — меняет её связывание (linkage) с внешнего на внутреннее, что похоже по эффекту на анонимное пространство имён:

  • Глобальная переменная без static: другие единицы трансляции могут получить к ней доступ через объявление extern;
  • Статическая глобальная переменная/функция: видима только в текущей единице трансляции; даже объявление extern не сможет на неё сослаться (undefined reference).

В современном C++ для выражения «видно только в этом файле» рекомендуется использовать анонимное пространство имён (оно работает и для типов), однако понимать этот аспект static очень важно при чтении старого кода.

// file1.cpp
static int hidden = 42;          // 内部链接:其他文件 extern 不可见
int visible = 1;                 // 外部链接:其他文件可 extern 访问

namespace {                      // 匿名命名空间:等价且更现代的写法
    int also_hidden = 7;
}

Частые вопросы

Чем static-глобальная переменная отличается от обычной?

Внутренней компоновкой: static-глобальная видна только в своей единице трансляции. Обычная видна из других файлов и провоцирует конфликты имён.

Когда инициализируется static-локальная переменная?

Один раз, при первом проходе через определение; с C++11 — потокобезопасно. Живёт до конца программы.

Может ли static-метод обращаться к не-статическим полям?

Нет: у него нет this, доступны только статические члены.

Похожие темы

Комментарии