Члены класса могут объявляться с ключевым словом static. В данном контексте его значение сходно с тем, которое оно имеет в С. Когда член класса объявляется как статический, то тем самым компилятору дается указание, что должна существовать только одна копия этого члена, сколько бы объектов этого класса ни создавалось. Статический член используется совместно всеми объектами данного класса. Все статические данные инициализируются нулями при создании первого объекта, и другая инициализация не предусмотрена.
При объявлении статического члена данных класса этот член не определяется, а просто резервируется место в классе. Затем необходимо определить статические члены класса глобально вне класса. Это делается путем нового объявления статической переменной, причем используется оператор области видимости для того, чтобы идентифицировать тот класс, к которому принадлежит переменная. Это необходимо для того, чтобы под статическую переменную была выделена память.
В качестве примера рассмотрим следующую программу:
#include <iostream.h>
class counter
{
static int count;
public:
void setcount(int i) { count = i; }
void showcount() { cout << count << " ";}
};
int counter::count; // определение count
int main()
{
counter a, b;
a.showcount(); // выводит 0
b.showcount(); // выводит 0
a.setcount(10); // установка статического count в 10
a.showcount(); // выводит 10
b.showcount(); // также выводит 10
return 0;
}
В первую очередь обратим внимание на то, что статическая переменная целого типа count объявляется в двух местах: в классе counter и затем — как глобальная переменная. Компилятор инициализирует count нулем. Именно поэтому первый вызов showcount() выводит в качестве результата нуль. Затем объект а устанавливает count равным 10. После этого оба объекта а и b выводят с помощью функции showcount() одну и ту же величину, равную 10. Поскольку существует только одна копия count, используемая совместно объектами а и b, то на экран выводится в обоих случаях значение 10.
ПАМЯТКА: Когда член класса объявляется статическим, тем самым определяется, что будет создаваться только одна копия этого члена, которая будет затем совместно использоваться всеми объектами этого класса.
Также можно иметь статические функции-члены. Статические функции-члены не могут прямо ссылаться на нестатические данные или нестатические функции, объявленные в их классе. Причиной тому является отсутствие для них указателя this, так что нет способа узнать, с какими именно нестатическими данными работать. Например, если имеется два объекта класса, содержащие статическую функцию f(), и если f() пытается получить доступ к нестатической переменной var, определенной этим классом, то как можно определить, какую именно копию var следует использовать? Компилятор не может решить такую проблему. В этом заключается причина того, что статические функции могут обращаться только к другим статическим функциям или статическим данным. Также статические функции не могут быть виртуальными или объявляться с модификаторами const или volatile. Статическая функция может вызываться либо с использованием объекта класса, либо с использованием имени класса и оператора области видимости. Тем не менее, не надо забывать, что даже при вызове статической функции с использованием объекта, ей не передается указатель this.
Следующая короткая программа иллюстрирует один из многих способов использования статических функций. Достаточно распространенной является ситуация, когда требуется обеспечить доступ к ограниченному ресурсу, например, такому, как совместно используемый файл в сети. Как иллюстрирует эта программа, использование статических данных и функций служит методом, посредством которого объект может проверить статус ресурса и получить к нему доступ, если это возможно.
static void set_access(enum access_t a) { acs = a; }
static enum access_t get_access() { return acs; }
};
enum access_t access::acs; // определение acs
int main()
{
access obj1, obj2;
access::set_access(locked); // вызов с использованием имени класса
// ... код
// может ли obj2 обращаться к ресурсу
if(obj2.get_access()==unlocked)
{
access::set_access(in_use); // вызов с помощью имени класса
cout << "Access resource.\n";
}
else cout << "Locked.\n";
return 0;
}
При запуске этой программы на экране появится «Locked». Обратим внимание, что функция set_access() вызвана с именем класса и оператором области видимости. Функция get_access() вызвана с объектом и оператором «точка». При вызове статической функции может использоваться любая из этих форм и обе они дают одинаковый эффект.
Как отмечалось, статические функции имеют прямой доступ только к другим статическим функциям или статическим данным в пределах одного и того же класса. В приведенном выше примере статические функции set_access() и get_access() работают только со статической переменной acs.
В прошлой главе кратко обсуждалась перегрузка функций и операторов, являющаяся важнейшей особенностью языка C++. В этой главе мы подробно рассмотрим вопросы, связанные с перегрузкой. Также в ходе этого обсуждения будут затронуты и смежные вопросы.
Хотя конструкторы и предназначены для решения особо важных задач, они мало отличаются от обычных функций и могут быть перегружены. Как было показано в примере пункта 3.11, для перегрузки конструктора класса достаточно просто объявить различные его формы. Как будет показано далее, во многих случаях перегрузка конструкторов позволяет добиться определенных преимуществ.
Начнем с примера. В следующей программе объявляется класс timer, действующий как таймер обратного отсчета. При создании объекта типа timer ему присваивается начальное значение времени. В результате вызова функции run() таймер начинает отсчет в сторону уменьшающихся значений, пока не достигнет значения 0. В данном примере конструктор перегружен для того, чтобы можно было указывать время в секундах с помощью целого числа или строки, или в минутах и секундах, если указываются два целых числа.
Эта программа использует библиотечную функцию clock(), возвращающую число тиков, прошедших с момента запуска программы. Поделив это значение на макрос CLOCKS_PER_SEC, получаем значение в секундах. Прототип clock() и макрос CLOCKS_PER_SEC содержатся в заголовочном файле time.h.
Как можно видеть, при создании объектов a, b и с внутри функции main() они получают начальное значение с использованием трех различных методов, поддерживаемых перегруженными конструкторами. Каждый из них позволяет провести инициализацию для соответствующих данных.