Урок №124. Статические переменные-члены класса

  Юрий  | 

  |

  Обновл. 24 Сен 2021  | 

 81882

 ǀ   9 

На этом уроке мы рассмотрим использование статических переменных-членов класса в языке С++.

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

Из урока №51 мы узнали, что статические переменные сохраняют свои значения и не уничтожаются даже после выхода из блока, в котором они объявлены, например:

Результат выполнения программы:

1
2
3

Обратите внимание, s_id сохраняет свое значение после каждого вызова функции generateID().

Ключевое слово static имеет другое значение, когда речь идет о глобальных переменных — оно предоставляет им внутреннюю связь (что ограничивает их видимость/использование за пределами файла, в котором они определены). Поскольку использование глобальных переменных — это зло, то ключевое слово static в этом контексте используется не очень часто.

В языке C++ ключевое слово static можно использовать в классах: статические переменные-члены и статические методы. Мы поговорим о статических переменных-членах на этом уроке, а о статических методах на следующем.

Прежде чем мы перейдем к ключевому слову static с переменными-членами класса, давайте сначала рассмотрим следующий класс:

При создании объекта класса, каждый объект получает свою собственную копию всех переменных-членов класса. В этом случае, поскольку мы объявили два объекта класса Anything, у нас будет две копии m_value: first.m_value и second.m_value. Это разные значения, следовательно, результат выполнения программы:

4
3

Переменные-члены класса можно сделать статическими, используя ключевое слово static. В отличие от обычных переменных-членов, статические переменные-члены являются общими для всех объектов класса. Рассмотрим следующую программу:

Результат выполнения программы:

4
4

Поскольку s_value является статической переменной-членом, то она является общей для всех объектов класса Anything. Следовательно, first.s_value — это та же переменная, что и second.s_value. Вышеприведенная программа показывает, что к значению, которое мы установили через первый объект, можно получить доступ и через второй объект.

Статические члены не связаны с объектами класса


Хотя вы можете получить доступ к статическим членам через разные объекты класса (как в примере, приведенном выше), но, оказывается, статические члены существуют, даже если объекты класса не созданы! Подобно глобальным переменным, они создаются при запуске программы и уничтожаются, когда программа завершает свое выполнение.

Следовательно, статические члены принадлежат классу, а не объектам этого класса. Поскольку s_value существует независимо от любых объектов класса, то доступ к нему осуществляется напрямую через имя класса и оператор разрешения области видимости (в данном случае, через Anything::s_value):

В вышеприведенном фрагменте доступ к s_value осуществляется через имя класса, а не через объект этого класса. Обратите внимание, мы даже не создавали объект класса Anything, но мы все равно имеем доступ к Anything::s_value и можем использовать эту переменную-член.

Определение и инициализация статических переменных-членов класса

Когда мы объявляем статическую переменную-член внутри тела класса, то мы сообщаем компилятору о существовании статической переменной-члене, но не о её определении (аналогией является предварительное объявление). Поскольку статические переменные-члены не являются частью отдельных объектов класса (они обрабатываются аналогично глобальным переменным и инициализируются при запуске программы), то вы должны явно определить статический член вне тела класса — в глобальной области видимости.

В программе, приведенной выше, это делается следующей строкой кода:

Здесь мы определили статическую переменную-член класса и инициализировали её значением 3. Если же инициализатор не предоставлен, то C++ инициализирует s_value значением 0.

Обратите внимание, данное определение статического члена не подпадает под действия спецификаторов доступа: вы можете определить и инициализировать s_value, даже если он будет private (или protected).

Если класс определен в заголовочном файле, то определение статического члена обычно помещается в файл с кодом класса (например, в Anything.cpp). Если класс определен в файле .cpp, то определение статического члена обычно пишется непосредственно под классом. Не пишите определение статического члена класса в заголовочном файле (подобно глобальным переменным). Если этот заголовочный файл подключают больше одного раза, то вы получите несколько определений одного члена, что приведет к ошибке компиляции.

Инициализация статических переменных-членов внутри тела класса


Есть несколько обходных путей определения статических членов внутри тела класса. Во-первых, если статический член является константным интегральным типом (к которому относятся и char, и bool) или константным перечислением, то статический член может быть инициализирован внутри тела класса:

Поскольку здесь статическая переменная-член является целочисленной константой, то дополнительной строки явного определения вне тела класса уже не требуется.

Во-вторых, начиная с C++11 статические члены constexpr любого типа данных, поддерживающие инициализацию constexpr, могут быть инициализированы внутри тела класса:

Использование статических переменных-членов класса

Зачем использовать статические переменные-члены внутри классов? Для присваивания уникального идентификатора каждому объекту класса (как вариант):

Результат выполнения программы:

1
2
3

Поскольку s_idGenerator является общим для всех объектов класса Anything, то при создании нового объекта класса Anything конструктор захватывает текущее значение s_idGenerator, а затем увеличивает его для следующего объекта. Это гарантирует уникальность идентификаторов для каждого созданного объекта класса Anything.

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


Оценить статью:

Звёзд: 1Звёзд: 2Звёзд: 3Звёзд: 4Звёзд: 5 (324 оценок, среднее: 4,93 из 5)
Загрузка...

Комментариев: 9

  1. Виктор:

    Как я понял статическая приватная переменная в основном теле не присвоится.

    1. Valera:

      переменная находится в private секции класса

  2. Eugene:

    Ещё удобно использовать такие переменные для сравнения, чтобы не захламлять глобальное пространство, если они нужны только в области видимости данного класса. Например, объявить статичную переменную int MAX_VALUE = 100, в private секции класса, и далее в каком-либо методе, при необходимости, сравнивать так : if (current_value != MAX_VALUE){…}

  3. Woland:

    С типом int работает, а с std::string ругается:

    Пишет, что не может иметь инициализатор внутри класса. Почему?
    Оно то понятно, что std::string это уже не просто тип, а класс. Но по логике он должен быть полноценно реализован.

    1. Senoron:

      "Eсли статический член является константным интегральным типом (к которому относятся и char, и bool) или константным перечислением, то статический член может быть инициализирован внутри тела класса"

      В вашем классе std::string не является не int, char или bool и также не перечислением, поэтому нужно инициализировать переменную вне тела класса.

    2. Grave18:

      Можно использовать string_view:

  4. Алексей:

    Разобрались, ок, а вот пустить бы это на практику.
    Ух, пошла бы забава на пару часов.

    Ничего, будет тест итоговый, там успею попрактиковаться, еще надоест)

  5. Александр:

    Спасибо за ваш труд Юрий!

    1. Фото аватара Юрий:

      Пожалуйста 🙂

Добавить комментарий для Юрий Отменить ответ

Ваш E-mail не будет опубликован. Обязательные поля помечены *