Почему?
Потому что нам нравится инкапсуляция и модульность, которые мы получаем при использовании объектно-ориентированных шаблонов.
Вы можете рассматривать static
функции-члены класса и переменные как способ инкапсуляции того, что в противном случае было бы глобальными функциями и переменными - которые могут конфликтовать.
Ниже не существует большой разницы между простым старым файлом C ++ с некоторыми функциями, объявленными и реализованными в нем, и классом, заполненным только stati c функциями. Разница в том, что мы можем получить чистый доступ к набору функций, прикрепленных к родительскому классу.
Пример: Предположим, вы хотите создать новый класс, такой как MyMediumInteger
, и вы хотите, чтобы разработчики определить, какое максимальное количество он может держать. Эта информация применима для каждого экземпляра MyMediumInteger
, независимо от состояния переменных закрытого члена. Поэтому имеет смысл раскрыть эту информацию, не заставляя разработчика создавать экземпляр класса. Ваши варианты включают в себя определение чего-то глобального, такого как #define MYMEDIUMINTEGER_MAX ...
, которое может столкнуться с определением, имеющим то же имя в другом модуле, или создание функции static
, возвращающей максимальный размер, вызываемой аккуратно через
MyMediumInteger::maxSize()
.
Пример кода:
/***
* Use a static class member function (or variable)
*/
class MyMediumInteger
{
public:
static unsigned long maxSize() { return pow(2, 32) - 1; };
};
auto maxSize = MyMediumInteger::maxSize();
/**
* Alternative approaches.
* Note: These could all collide with other #defines or symbols declared/implemented in other modules.
* Note: These are both independant sources of information related to a class - wouldn't it be nicer if they could just belong to the class instead?
*/
/***
* Use a #define
*/
#define MYMEDIUMINTEGER_MAX (pow(2, 32) - 1)
auto maxSize = MYMEDIUMINTEGER_MAX;
/**
* Use a global function or variable
*/
static unsigned long getMyMediumIntegerMaxSize()
{
return pow(2, 32) - 1;
}
auto maxSize = getMyMediumIntegerMaxSize();
Предупреждение: Stati c переменные и функции-члены имеют некоторые подводные камни: глобальные переменные - поскольку они сохраняются в экземплярах классов, вызов и присвоение им могут вызвать неожиданные побочные эффекты (потому что static
переменные-члены изменяются для всех, а не только для вас). Распространенной ошибкой является добавление большого количества статических c функций-членов и переменных для управления - например, статическое ведение списка всех других экземпляров класса, который добавляется в конструктор каждого класса. Часто этот тип кода может быть реорганизован в родительский класс, задачей которого является просто управление экземплярами дочернего класса. Последний подход гораздо более тестируем и сохраняет модульность - например, кто-то другой может прийти и написать другую реализацию кода управления, не добавляя и не переписывая реализацию своего дочернего класса.
Как?
В функциях-членах класса C ++ фактически не хранятся в экземплярах классов, они хранятся отдельно и вызываются для экземпляров классов. Следовательно, несложно представить, как в это вписываются функции stati c - они объявляются так же, как и обычные функции-члены класса, только с ключевым словом static
, который говорит: «Мне не нужен класс экземпляр для запуска ". Следствием этого является невозможность доступа к переменным или методам членов класса.