- Основы ООП: абстракция
- Основы ООП: инкапусляция
- Основы ООП: наследование
- Основы ООП: полиморфизм
- Основы ООП: композиция (дизайн)
- Определение класса
- Права доступа
- Конструкторы: по умолчанию, параметрический, копирования
- Деструкторы
- Ссылка на себя: this
- Наследование
- Динамическое создание объектов: new и delete
- Указатели
- Виртуальные методы (virtual и override)
- Статические члены
- Вложенные типы, пространства имен и оператор контекста '::'
- Разнесение объявления и определения класса
- Абстрактные классы и интерфейсы
- Перегрузка операторов
- Приведение объектных типов: dynamic_cast и указатель void *
- Указатели, ссылки и const
- Управление наследованием: final и delete
Статические члены
До сих пор мы рассматривали поля и методы класса, которые описывают состояние и поведение объектов данного класса. Однако в программах бывает необходимо хранить некоторые атрибуты или выполнять операции для класса целиком, а не для его объектов. Такие свойства класса называются статическими и описываются с помощью ключевого слова static, добавляемого перед типом. Они также поддерживаются в структурах и объединениях.
Например, мы можем подсчитывать количество фигур, созданных пользователем в программе рисования. Для этого в классе Shape опишем статическую переменную count (Shapes5.mq5).
class Shape
|
Она определена в секции private и потому недоступна извне.
Для чтения текущего значения счетчика предусмотрен публичный статический метод getCount(). В принципе, поскольку статические члены определены в контексте класса, они получают ограничения видимости согласно модификатору секции, в которой расположены.
Будем увеличивать счетчик на 1 в параметрическом конструкторе Shape, а конструктор по умолчанию уберем. Таким образом, каждый экземпляр фигуры любого производного типа окажется учтенным.
Обратите внимание, что статическую переменную необходимо явным образом определить (и опционально проинициализировать) вне блока класса:
static int Shape::count = 0; |
Статические переменные класса похожи на глобальные переменные и статические переменные внутри функций (см. раздел Статические переменные) в том плане, что создаются при запуске программы и удаляются перед её выгрузкой. Поэтому, в отличие от переменных объекта, они должны существовать в виде единственного экземпляра изначально.
В данном случае инициализацию нулем можно опустить, потому что, как мы знаем, глобальные и статические переменные по умолчанию получают нулевые значения. Статическими могут быть и массивы.
В определении статической переменной мы видим использование специального оператора выбора контекста '::'. С помощью него формируется полностью квалифицированное имя переменной. Слева от '::' стоит имя класса, к которому относится переменная, а справа — её идентификатор. Очевидно, что полное имя необходимо, потому что внутри разных классов могут быть описаны статические переменные с одним и тем же идентификатором, и нужен способ однозначно обращаться к каждой из них.
Тот же оператор '::' используется для доступа не только к публичным статическим переменным класса, но и методам. В частности, для того чтобы вызвать метод getCount в функции OnStart используем следующий синтаксис — Shape::getCount():
void OnStart()
|
Поскольку сейчас генерируется заданное количество фигур (10), мы можем убедиться, что счетчик работает правильно.
При наличии объекта класса, можно обратиться к статическому методу или свойству через привычное разыменование (например, shape.getCount()), но такая запись может вводить в заблуждение (поскольку скрывает тот факт, что обращения к объекту на самом деле не происходит).
Отметим, что создание производных классов никак не влияет на статические переменные и методы: они всегда приписаны к тому классу, в котором были определены. Наш счетчик — единый для всех классов фигур, производных от Shape.
Внутри статических методов нельзя использовать this, поскольку они выполняются без привязки к конкретному объекту. Также из статического метода нельзя напрямую, без разыменования какой-либо переменной объектного типа, вызвать обычный метод класса или обращаться к его полю. Например, если вызвать draw из getCount, получим ошибку "доступ к нестатическому члену или функции":
static int getCount()
|
По той же причине статические методы не могут быть виртуальными.
Можно ли, пользуясь статическими переменными, подсчитать не общее количество фигур, а их статистику в разбивке по типам? Да, можно. Эта задача оставлена для самостоятельной проработки. Желающие могут найти один из примеров реализации в скрипте Shapes5stats.mq5.