Статические члены класса
Класс – это тип, а не объект данных, и в каждом объекте класса имеется своя собственная копия данных – членов этого класса. Однако некоторые типы требуется реализовать так, что все объекты этого типа могут совместно использовать некоторые данные. Такие совместно используемые данные должны быть описаны как часть класса.
Статические данные относятся ко всем объектам класса. Такие данные используются, если
- требуется контроль общего количества объектов класса;
- требуется одновременный доступ ко всем объектам или части их;
- требуется разделение объектами общих ресурсов.
В этом случае в определение класса могут быть введены статические члены.
Статические члены описываются с помощью ключевого слова static , которое может использоваться при объявлении член-данных и член-функций.
Такие члены классов называются статическими, и независимо от количества объектов данного класса, существует только одна копия статического элемента.
Обращение к статическому элементу осуществляется с помощью оператора разрешения контекста и имени класса:
ИмяКласса :: ИмяЭлемента
Если x – статическое член-данное класса cl, то к нему можно обращаться как
cl::x
При этом не имеет значения количество объектов класса cl.
Аналогично можно обращаться к статической член-функции:
Статические член-данные (или поля) класса можно рассматривать как глобальную переменную класса. Но в отличие от обычных глобальных переменных на статические члены распространяются правила видимости private и public . Поместив статическую переменную в часть private , можно ограничить ее использование.
Объявление статического члена в объявлении класса не является определением, то есть это объявление статического члена не обеспечивает распределения памяти и инициализацию.
Инициализация статических полей
Статические поля нельзя инициализировать в теле класса, а также в методах. Статические поля должны инициализироваться аналогично глобальным переменным в области видимости файла:
Результат выполнения
Что такое ключевое слово static?
Чтобы получить доступ к членам класса в Java, нужно сначала создать экземпляр класса, а затем вызвать членов класса с помощью переменной экземпляра. Но иногда нужно получить доступ к членам класса, не создавая никаких переменных.
В таком случае можно воспользоваться ключевым словом static , то есть объявить членов класса статическими. В Java большинство членов служебного класса являются статическими. Вот несколько примеров.
- java.util.Objects содержит статические служебные операции для метода объекта.
- java.util.Collections состоит исключительно из статических методов, которые работают с коллекциями или возвращают их.
Где можно употреблять ключевое слово static?
Мы можем использовать это ключевое слово в четырех контекстах:
- статические методы;
- статические переменные;
- статические вложенные классы;
- статические блоки.
Рассмотрим подробнее каждый из перечисленных пунктов.
Статические методы
Статические методы также называются методами класса, потому что статический метод принадлежит классу, а не его объекту. Кроме того, статические методы можно вызывать напрямую через имя класса.
В приведенном выше примере метод add — статический, поэтому его можно вызывать напрямую с именем класса. Нет необходимости создавать новый экземпляр класса StaticExample . Но метод multiply не является статическим. Таким образом, для нестатического метода необходимо создать новый экземпляр класса StaticExample .
Статические переменные
При создании объектов класса в Java каждый из них содержит собственную копию всех переменных класса.
Однако, если мы объявим переменную статической, все объекты класса будут использовать одну и ту же статическую переменную. Это связано с тем, что, как и статические методы, статические переменные также связаны с классом. И объекты класса для доступа к статическим переменным создавать не нужно. Например:
В приведенном выше примере normalVariable — переменная класса, а staticVariable — статическая переменная. Если вы объявите переменную, как показано ниже:
Это похоже на доступ к статической переменной через имя класса:
Здесь статические переменные — переменные уровня класса. Поэтому, если вы обращаетесь к статическим переменным через переменную экземпляра объекта, то это отражается на соответствующей статической переменной уровня класса. Таким образом, статические переменные всегда имеют одно и то же значение. Но переменная экземпляра сохраняет отдельное значение в каждом экземпляре объекта. Таким образом, приведенный выше фрагмент кода выведет:
Статические переменные — редкость в Java. Вместо них применяют статические константы. Они определяются ключевым словом static final и представлены в верхнем регистре. Вот почему некоторые предпочитают использовать верхний регистр и для статических переменных.
Статические блоки
Здесь мы видим статический блок с синтаксисом:
Статические блоки применяют для инициализации статических переменных. Статический блок выполняется только один раз, когда класс загружается в память. Это происходит, если в коде запрашивается либо объект класса, либо статические члены этого класса.
Класс может содержать несколько статических блоков, а каждый из них выполняется в той же последовательности, в которой они написаны в коде.
Вывод приведенного выше фрагмента кода выглядит следующим образом, потому что переменная staticVariable обновлена вторым значением статического блока.
Вложенный статический класс
В Java можно объявить класс внутри другого класса. Такие классы называются вложенными классами. Они бывают двух типов: статические и нестатические.
Вложенный класс является членом заключающего его класса. Нестатические вложенные классы (внутренние классы) имеют доступ к другим членам заключающего класса, даже если они объявлены приватными. Статические вложенные классы не имеют доступа к другим членам заключающего класса.
Внутренний класс по умолчанию содержит неявную ссылку на объект внешнего класса. Если вы создадите экземпляр этого объекта из кода внешнего класса, все будет сделано за вас. Если вы поступите иначе, вам необходимо предоставить объект самостоятельно.
Со статическим внутренним классом все иначе. Можно сказать, что если у внутреннего класса нет причин для доступа к внешнему, вы должны сделать его статическим по умолчанию.
В приведенном выше примере видно, что внутренний класс MyInnerClass может получить доступ ко всем методам и переменным внешнего, включая приватные переменные. Статическому внутреннему классу, напротив, недоступны какие-либо методы или переменные внешнего класса.
Где в памяти Java хранятся статические классы и переменные?
Вплоть до 8-й версии Java статические методы и переменные хранились в пространстве permgen. Но потом было введено новое пространство памяти, называемое метапространством — в нем хранятся все эти имена и поля класса, методы класса с байт-кодом методов, пул констант, JIT-оптимизации и т. д. Причина удаления permgen в Java 8.0 в том, что очень сложно предсказать необходимый размер permgen.
Зачем нужно ключевое слово final?
В Java при объявлении сущности можно воспользоваться ключевым словом final . Оно предполагает, что значение не может быть изменено в будущем. Ключевое слово final может применяться для переменной, метода и класса.
- Конечная переменная предназначена для создания постоянных значений.
- Конечный метод предотвращает переопределение метода.
- Конечный класс предотвращает наследование.
Что такое конечная переменная и когда ей стоит воспользоваться?
Когда переменная объявляется с помощью ключевого слова final , ее значение не может быть изменено. По сути, это константа. Это также означает, что конечную переменную необходимо инициализировать. Если конечная переменная является ссылкой, то ее нельзя будет повторно привязать к другому объекту, но внутреннее состояние объекта, на которое указывает эта ссылочная переменная, может быть изменено, т.е. вы можете добавлять или удалять элементы из конечного массива или конечной коллекции. Рекомендуется именовать конечные переменные целиком в верхнем регистре и с подчеркиванием для разделения слов.
Существует три способа инициализации конечной переменной.
- Инициализировать конечную переменную, когда она будет объявлена. Это самый распространенный подход. Если конечная переменная не инициализирована при объявлении, она называется пустой конечной переменной. Ниже приведены два способа инициализации пустой конечной переменной.
- Пустая конечная переменная может быть инициализирована внутри блока инициализатора экземпляра или внутри конструктора. Если в классе несколько конструкторов, то переменную необходимо инициализировать во всех из них, в противном случае во время компиляции появится ошибка.
- Пустая конечная статическая переменная может быть инициализирована в статическом блоке.
Когда следует применять конечную переменную
Единственное различие между обычной и конечной переменной в том, что первой можно переприсвоить значение, а второй — нельзя. Следовательно, конечные переменные должны использоваться только для значений, которые необходимо сохранять постоянными на протяжении выполнения программы.
Где использовать конечные классы
Когда класс объявляется с ключевым словом final , он называется конечным. Конечный класс не может быть расширен (унаследован). У него есть два применения.
Первое, безусловно, заключается в предотвращении наследования, так как конечные классы не могут быть расширены. Например, все классы-оболочки, такие как Integer , Float и т. д. — конечные классы.
При попытке расширить конечный класс компилятор выдаст следующее исключение:
Другое применение final с классами заключается в создании неизменяемого класса, подобного предопределенному классу String . Нельзя сделать класс неизменяемым, не сделав его конечным.
Когда использовать конечные методы
Когда метод объявляется с ключевым словом final , он называется конечным методом. Такой метод не может быть переопределен. Это присутствует в классе Object — ряд его методов конечные. С ключевым словом final объявляются методы, для которых необходимо следовать одной и той же реализации во всех производных классах. Фрагмент ниже иллюстрирует метод с ключевым словом final :
Здесь происходит попытка переопределить метод final из родительского класса. Java этого не позволяет и выдает следующее исключение:
Статические классы, методы, переменные. Статические конструкторы. Ключевое слово static
Чтобы класс (метод, переменная) был статическим, перед его объявлением ставится ключевое слово static .
⇑
2. Понятие статического класса. Какие особенности использования статических классов в программах на C#? Ключевое слово static
С точки зрения синтаксиса C# статический класс – это класс, который объявляется с ключевым словом static.
Общая форма объявления статического класса:
где ClassName – имя статического класса.
⇑
3. Свойства статического класса
Реализация программного кода статического класса ничем не отличается от программного кода обычного класса за исключением двух основных свойств. В сравнении с нестатическим классом, статический класс имеет следующие свойства (отличия):
- нельзя создавать объекты статического класса;
- статический класс должен содержать только статические члены.
⇑
4. Примеры, которые демонстрируют свойства статического класса
Пример 1. Пусть задан статический класс MyStaticClass . В этом классе объявляется один статический член с именем d .
Если попробовать создать объект статического класса
то возникнет ошибка компиляции с сообщением:
Пример 2. Данный пример демонстрирует правило, что статический класс должен содержать только статические члены. Пусть задан статический класс MyStaticClass . Если в статическом классе MyStaticClass попробовать объявить нестатический член d целого типа
то во время компиляции компилятор выдаст следующую ошибку:
⇑
5. Примеры статических классов
Пример 1. В данном примере демонстрируется использование статической переменной в статическом классе. Объявляется статический класс с именем Count , в котором помещается одна статическая переменная count . Эта статическая переменная есть счетчиком, который совместно используется в нестатических методах Add1() , Add2() другого класса Methods .
Программный код классов Count и Methods следующий:
В нижеследующем коде продемонстрированы обращения к переменной count класса Count и изменение значения этой переменной из методов Add1() , Add2() .
Как видно из примера, статический член данных Count.count есть общим для методов Add1() , Add2() класса Methods . Если бы в программе были реализованы другие классы с некоторыми методами, то к этому члену данных можно было бы обращаться из этих методов.
Если в данном примере класс Count объявить как нестатический (без ключевого слова static )
то результат работы программы не изменится. Статическую переменную Count.count можно использовать как общий ресурс.
Пример 2. Пример демонстрирует использование статического метода в нестатическом классе. Статический метод выступает общим ресурсом, который выполняет некоторую общую работу.
В примере объявляется статический метод AbsComplex() , находящий модуль комплексного числа. Метод объявляется в классе ComplexLibrary . Также объявляются 2 класса, которые содержат методы, использующие метод AbsComplex() в своих вычислениях.
Использование методов классов CalcComplex1 , CalcComplex2 может быть следующим:
⇑
6. Какие преимущества применения статических классов, методов и переменных в программах на C#?
Статические классы, методы и переменные эффективны в следующих случаях:
- если нужно создать так называемые методы расширения. Методы расширения используются для расширения функций класса. Эти методы являются статическими;
- если в программе есть некоторый общий ресурс, к которому могут иметь обращение методы разных классов что обрабатывают данный ресурс (читают или изменяют его значение). Этот общий ресурс объявляется как статическая переменная. Например, таким ресурсом может быть некоторый счетчик вызовов, метод что реализует уникальную обработку, уникальная файловая переменная (ресурс) и т.п.;
- статические классы являются эффективными, когда нужно объединить между собой группы статических методов;
- если нужно использовать общие скрытые ( private ) данные класса и организовывать доступ к этим данным из статических и нестатических методов.
⇑
7. Чем отличается вызов статического метода от нестатического?
В любом нестатическом классе могут быть объявлены как статические методы, так и нестатические. Отличие между вызовом статического и нестатического метода класса состоит в следующем:
- чтобы вызвать нестатический метод класса, нужно создать экземпляр (объект) этого класса. Статический метод вызывается без создания объекта класса – перед именем метода указывается имя класса, в котором этот статический метод объявлен.
Например. Задан нестатический класс Sqr , содержащий следующие два метода, которые возвращают квадрат целочисленного значения:
- GetSqr() – нестатический метод;
- GetSqrStatic() – статический метод.
Ниже демонстрируется вызов и использование этих методов:
Как видно из вышеприведенного кода, чтобы вызвать статический метод некоторого класса, перед его именем нужно указать имя этого класса.
⇑
8. Можно ли объявить скрытый ( private ) статический член в некотором классе?
Да можно. В этом случае этот статический член класса будет доступным только в границах этого класса, то есть из методов этого класса. Доступ из методов других классов будет невозможен.
⇑
9. Можно ли объявлять статический член класса с модификатором доступа protected ?
Да можно. В этом случае, доступ к статического члену класса будут иметь методы класса, которые унаследованы от данного класса.
Например. Задан класс A , содержащий один статический член a , который объявлен как protected . Также задан класс B , который наследует (расширяет) класс A . Из метода SomeMethod() класса B осуществляется доступ к protected -переменной класса A .
⇑
10. Может ли нестатический класс содержать статические переменные и статические методы?
Да может. Примеры использования статических методов в нестатическом классе приведены в пунктах 5 и 7.
⇑
11. Можно ли объединять статические и нестатические методы в одном классе?
Да, можно. Но только в нестатическом классе. Если класс объявлен как статический, то все методы и переменные класса должны быть также статическими (см. п. 4 — Пример 2).
Например. Пример демонстрирует объединение статического и нестатического методов класса для доступа к скрытой статической переменной t в классе. Объявляется нестатический класс CMyClass , содержащий статическую переменную, статический и нестатический методы доступа к ней.
В нижеследующем коде продемонстрирован доступ к скрытой статической переменной t класса CMyClass
Данный пример хорошо демонстрирует, как можно организовать работу с общими, скрытыми данными класса.
⇑
12. Можно ли в статическом методе статического класса создать объект нестатического класса?
Да, можно. Классический пример этому, функция Main() для консольных приложений. Эта функция объявлена как static . Однако, создавать экземпляры любых нестатических классов в этой функции можно.
⇑
13. Что такое статические конструкторы? Пример
Статические конструкторы позволяют инициализировать статические переменные класса.
Пример. Демонстрируется объявление статического конструктора в классе.
Демонстрация работы класса CCount в некотором методе
⇑
14. Какие правила (особенности) использования статических конструкторов?
При использовании статических конструкторов нужно обратить внимание на следующие правила:
- перед статическим конструктором должно указываться ключевое слово static ;
- в классе может быть только один статический конструктор. Это значит, что статический конструктор нельзя перегружать;
- статические конструкторы вызываются когда создается экземпляр класса или когда идет обращение к статическому члену этого класса;
- статический конструктор в программе выполняется только один раз. Даже если создать несколько экземпляров класса, содержащего статический конструктор, все равно этот статический конструктор выполнится только один раз;
- у статических конструкторов не может быть модификаторов доступа ( public , private );
- если в классе есть статический конструктор и конструктор создающий экземпляр (или несколько конструкторов), то статический конструктор вызывается первым;
- статический конструктор не может иметь параметров. Если попробовать создать параметризированный статический конструктор в классе, то возникнет ошибка компиляции.
⇑
15. Можно ли из статических конструкторов инициализировать нестатические переменные класса?
Нет, нельзя. Из статических конструкторов можно инициализировать только статические переменные этого же класса. Если в этом классе также объявляются нестатические переменные, то доступа к ним из статических конструкторов нету.
Где можно выполнять инициализацию статистической переменной класса

Всем привет. На одном из код-ревью я столкнулся с мыслью, что многие, а чего скрывать и я сам, не то чтобы хорошо понимаем когда нужно использовать ключевое слова static. В данной статье я хотел бы поделиться своими знаниями и информацией по поводу ключевого слова static. Статья будет полезна как начинающим программистам, так и людям, работающим с языком С++. Для понимания статьи у вас должны быть знания о процессе сборки проектов и владение языком С/С++ на базовом уровне. Кстати, static используется не только в С++, но и в С. В этой статье я буду говорить о С++, но имейте в виду, что всё то, что не связано с объектами и классами, в основном применимо и к языку С.
Что такое static?
Static — это ключевое слово в C++, используемое для придания элементу особых характеристик. Для статических элементов выделение памяти происходит только один раз и существуют эти элементы до завершения программы. Хранятся все эти элементы не в heap и не на stack, а в специальных сегментах памяти, которые называются .data и .bss (зависит от того инициализированы статические данные или нет). На картинке ниже показан типичный макет программной памяти.

Где используется?
Ниже приведена схема, как и где используется static в программе.

А теперь я постараюсь детально описать все то, что изображено на схеме. Поехали!
Статические переменные внутри функции
Статические переменные при использовании внутри функции инициализируются только один раз, а затем они сохраняют свое значение. Эти статические переменные хранятся в статической области памяти (.data или .bss), а не в стеке, что позволяет хранить и использовать значение переменной на протяжении всей жизни программы. Давайте рассмотрим две почти одинаковые программы и их поведение. Отличие в них только в том, что одна использует статическую переменную, а вторая нет.
Если не использовать static в строке 4, выделение памяти и инициализация переменной count происходит при каждом вызове функции counter(), и уничтожается каждый раз, когда функция завершается. Но если мы сделаем переменную статической, после инициализации (при первом вызове функции counter()) область видимости count будет до конца функции main(), и переменная будет хранить свое значение между вызовами функции counter().
Статические объекты класса
Статический объект класса имеет такие же свойства как и обычная статическая переменная, описанная выше, т.е. хранится в .data или .bss сегменте памяти, создается на старте и уничтожается при завершении программы, и инициализируется только один раз. Инициализация объекта происходит, как и обычно — через конструктор класса. Рассмотрим пример со статическим объектом класса.
В строке 3 мы создаем класс Base с конструктором (строка 5) и деструктором (строка 8). При вызове конструктора либо деструктора мы выводим название метода класса в консоль. В строке 14 мы создаем статический объект obj класса Base. Создание этого статического объекта будет происходить только при первом вызове функции foo() в строке 18.
Из-за того, что объект статический, деструктор вызывается не при выходе из функции foo() в строке 15, а только при завершении программы, т.к. статический объект разрушается при завершении программы. Ниже приведен пример той же программы, за исключением того, что наш объект нестатический.
Если мы уберем static при создании переменной в функции foo(), то разрушение объекта будет происходить в строке 15 при каждом вызове функции. В таком случае вывод программы будет вполне ожидаемый для локальной переменной с выделенной памятью на стеке:
Статические члены класса
В сравнении с предыдущими вариантами использования, статические члены класса немного сложнее для понимания. Давайте разберемся, почему. Предположим, у нас есть следующая программа:
В нашем примере мы создали класс А (строка 3) и класс В (строка 9) со статическими членами класса (строка 15). Мы предполагаем, что при создании объекта b в строке 19 будет создан объект a в строке 15. Так бы и произошло, если бы мы использовали нестатические члены класса. Но вывод программы будет следующим:
Причиной такого поведения является то, что статические члены класса не инициализируются с помощью конструктора, поскольку они не зависят от инициализации объекта. Т.е. в строке 15 мы только объявляем объект, а не определяем его, так как определение должно происходить вне класса с помощью оператора разрешения области видимости (::). Давайте определим члены класса B.
Теперь, после того как мы определили наш статический член класса в строке 18, мы можем увидеть следующий результат программы:
Нужно помнить, что член класса будет один для всех экземпляров класса B, т.е. если мы создали три объекта класса B, то конструктор статического члена класса будет вызван только один раз. Вот пример того, о чем я говорю:
Статические функции
Статические функции пришли в С++ из С. По умолчанию все функции в С глобальные и, если вы захотите создать две функции с одинаковым именем в двух разных .c(.cpp) файлах одного проекта, то получите ошибку о том, что данная функция уже определена (fatal error LNK1169: one or more multiply defined symbols found). Ниже приведен листинг трех файлов одной программы.
Для того чтобы исправить данную проблему, одну из функций мы объявим статической. Например эту:
В этом случае вы говорите компилятору, что доступ к статическим функциям ограничен файлом, в котором они объявлены. И он имеет доступ только к функции sum() из math.cpp файла. Таким образом, используя static для функции, мы можем ограничить область видимости этой функции, и данная функция не будет видна в других файлах, если, конечно, это не заголовочный файл (.h).
Как известно, мы не можем определить функцию в заголовочном файле не сделав ее inline или static, потому что при повторном включении этого заголовочного файла мы получим такую же ошибку, как и при использовании двух функций с одинаковым именем. При определении статической функции в заголовочном файле мы даем возможность каждому файлу (.cpp), который сделает #include нашего заголовочного файла, иметь свое собственное определение этой функции. Это решает проблему, но влечет за собой увеличение размера выполняемого файла, т.к. директива include просто копирует содержимое заголовочного файла в .cpp файл.
Статические функции-члены класса (методы)
Статическую функцию-член вы можете использовать без создания объекта класса. Доступ к статическим функциям осуществляется с использованием имени класса и оператора разрешения области видимости (::). При использовании статической функции-члена есть ограничения, такие как:
- Внутри функции обращаться можно только к статическим членам данных, другим статическим функциям-членам и любым другим функциям извне класса.
- Статические функции-члены имеют область видимости класса, в котором они находятся.
- Вы не имеете доступа к указателю this класса, потому что мы не создаем никакого объекта для вызова этой функции.
В классе A в строке 8 у нас есть статическая функция-член foo(). В строке 14, мы вызываем функцию используя имя класса и оператор разрешения области видимости и получаем следующий результат программы:
Из вывода видно, что никакого создания объекта нет и конструктор/деструктор не вызывается.
Если бы метод foo() был бы нестатическим, то компилятор выдал бы ошибку на выражение в строке 14, т.к. нужно создать объект для того, чтобы получить доступ к его нестатическим методам.
Заключение
В одной статье в интернете я нашел совет от автора – «Используйте static везде, где только можно». Я хотел бы написать, почему так делать не стоит, а стоит использовать только в случае необходимости.
Урок №124. Статические переменные-члены класса
На этом уроке мы рассмотрим использование статических переменных-членов класса в языке С++.
Статические переменные-члены класса
Из урока №51 мы узнали, что статические переменные сохраняют свои значения и не уничтожаются даже после выхода из блока, в котором они объявлены, например:
Результат выполнения программы:
Обратите внимание, s_id сохраняет свое значение после каждого вызова функции generateID().
Ключевое слово static имеет другое значение, когда речь идет о глобальных переменных — оно предоставляет им внутреннюю связь (что ограничивает их видимость/использование за пределами файла, в котором они определены). Поскольку использование глобальных переменных — это зло, то ключевое слово static в этом контексте используется не очень часто.
В языке C++ ключевое слово static можно использовать в классах: статические переменные-члены и статические методы. Мы поговорим о статических переменных-членах на этом уроке, а о статических методах на следующем.
Прежде чем мы перейдем к ключевому слову static с переменными-членами класса, давайте сначала рассмотрим следующий класс:
При создании объекта класса, каждый объект получает свою собственную копию всех переменных-членов класса. В этом случае, поскольку мы объявили два объекта класса Anything, у нас будет две копии m_value : first.m_value и second.m_value . Это разные значения, следовательно, результат выполнения программы:
Переменные-члены класса можно сделать статическими, используя ключевое слово static. В отличие от обычных переменных-членов, статические переменные-члены являются общими для всех объектов класса. Рассмотрим следующую программу:
Результат выполнения программы:
Поскольку 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. Нулевая инициализация
Она возникает тогда, когда начальное значение не может быть оценено во время компиляции, в этом случае все статические переменные либо инициализируются константами, либо нулём, что является большой проблемой, так как однозначно неизвестен вид инициализации. Такая переменная попадёт в динамическую память, но с константным выражением обычно инициализированной нулём, либо она будет в бинарнике с заданным константным выражением. Как следствие, это ведёт к появлению трудно выявляемых ошибок, так как исчезает возможность контроля корректности инициализированных значений.
Для того чтобы этого избежать и принудительно создать статическую переменную, используется спецификатор constexpr, который вычисляет выражение (или результат работы функции) на этапе компиляции при условии, что оно может быть вычислено. Например:
Но здесь есть свои подводные камни, о которых необходимо упомянуть. Мы все любим выражения подобно auto VER = std::string( «3.4.1» ) , так вот, выражения подобного вида с constexpr, например:
работать не будут, что и логично, так как класс std::string выделяет некоторый ресурс, который должен быть освобождён при уничтожении, в данном случае памяти. Следовательно, std::string( «3.4.1» ) не может быть константным выражением, вычисляемым во время компиляции. В замен мы вынуждены использовать const и за это платим перемещением из времени компиляции во время выполнения, т. е. переходим из статической в динамическую инициализацию.
3. Static-проблема порядка инициализации
Так как порядок инициализации статических переменных чётко не определён, возникает серьёзная проблема правильной инициализации, если значения находятся в разных модулях. Короче говоря, предположим, что у нас есть два static-объекта x и y, которые существуют в отдельных исходных файлах, скажем, x.cpp и y.cpp. Предположим далее, что инициализация для y объекта (обычно y — конструктор объекта) вызывает некоторый метод x объекта. Вот и все. Мы получили 50%-ную вероятность испортить программу.
Как правило, подобная проблема возникает из-за плохого проектирования проекта. Лучший способ её решить — это рефакторинг кода, чтобы разорвать зависимость инициализации глобальных переменных от единиц компиляции. Необходимо сделать модули автономными и стремиться к постоянной инициализации.
Если рефакторинг кода не подходит, возможно использовать идиому Construct On First Use. Основная идея состоит в том, чтобы спроектировать статические переменные, которые не являются константными выражениями (то есть теми, которые должны быть инициализированы во время выполнения) таким образом, чтобы они создавались при первом обращении к ним. Подобный подход часто называется синглтоном Мейера. Как пример:
B всегда будет инициализироваться в значение 14.
В заключении хотелось бы отметить, что в общем смысле вопрос правильной инициализации достаточно сложный. Этой теме посвящено множество публикаций и выступлений, особенно на фоне неоднозначного изменения в стандартах языка С++. Так, C++11 принёс концепцию «универсальной инициализации», которая привнесла ещё более сложные правила, и, в свою очередь, их перекрыли в C++14, C++17 и снова поменяют в C++20.
Статические члены класса
Класс – это тип, а не объект данных, и в каждом объекте класса имеется своя собственная копия данных – членов этого класса. Однако некоторые типы требуется реализовать так, что все объекты этого типа могут совместно использовать некоторые данные. Такие совместно используемые данные должны быть описаны как часть класса.
Статические данные относятся ко всем объектам класса. Такие данные используются, если
- требуется контроль общего количества объектов класса;
- требуется одновременный доступ ко всем объектам или части их;
- требуется разделение объектами общих ресурсов.
В этом случае в определение класса могут быть введены статические члены.
Статические члены описываются с помощью ключевого слова static , которое может использоваться при объявлении член-данных и член-функций.
Такие члены классов называются статическими, и независимо от количества объектов данного класса, существует только одна копия статического элемента.
Обращение к статическому элементу осуществляется с помощью оператора разрешения контекста и имени класса:
ИмяКласса :: ИмяЭлемента
Если x – статическое член-данное класса cl, то к нему можно обращаться как
cl::x
При этом не имеет значения количество объектов класса cl.
Аналогично можно обращаться к статической член-функции:
Статические член-данные (или поля) класса можно рассматривать как глобальную переменную класса. Но в отличие от обычных глобальных переменных на статические члены распространяются правила видимости private и public . Поместив статическую переменную в часть private , можно ограничить ее использование.
Объявление статического члена в объявлении класса не является определением, то есть это объявление статического члена не обеспечивает распределения памяти и инициализацию.
Инициализация статических полей
Статические поля нельзя инициализировать в теле класса, а также в методах. Статические поля должны инициализироваться аналогично глобальным переменным в области видимости файла:
Результат выполнения 