Программирование на C и C++

Онлайн справочник программиста на C и C++

Статические члены класса

Члены класса могут использоваться с ключевым словом 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 и затем — как глобальная переменная. Borland С++ иници­ализирует count нулем. Именно поэтому первый вызов showcount() выводит в качестве результата нуль. Затем объект а устанавливает count равным 10. После этого оба объекта а и b выводят с помощью функции showcount() одну и ту же величину, равную 10. Поскольку существует только одна копия count, используемая совместно объектами а и b, то на экран выводится в обоих слу­чаях значение 10.

ПАМЯТКА: Когда член класса объявляется статическим, тем самым определя­ется, что будет создаваться только одна копия этого члена, которая будет затем совместно использоваться всеми объектами этого класса.

Также можно иметь статические функции-члены. Статические функции-члены не могут прямо ссылаться на нестатические данные или нестатические функции, объявленные в их классе. Причи­ной тому является отсутствие для них указателя this, так что нет способа узнать, с какими именно нестатическими данными работать. Например, если имеется два объекта класса, содержащие стати­ческую функцию f(), и если f() пытается получить доступ к нестатической переменной var, опреде­ленной этим классом, то как можно определить, какую именно копию var следует использовать? Компилятор не может решить такую проблему. В этом заключается причина того, что статичес­кие функции могут обращаться только к другим статическим функциям или статическим данным. Также статические функции не могут быть виртуальными или объявляться с модификаторами const иди volatile. Статическая функция может вызываться либо с использованием объекта класса, либо с использованием имени класса и оператора области видимости. Тем не менее не надо забы­вать, что даже при вызове статической функции с использованием объекта, ей не передается указатель this.

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

#include <iostream.h>
enum access_t {shared, in_use, locked, unlocked};
// класс контролирует редкий ресурс
class access {
static enum access_t acs;
// ...
public:
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 out.\n";
// ...
return 0;
}

При запуске этой программы на экране появится «locked out». Обратим внимание, что функция set_access() вызвана с именем класса и оператором области видимости. Функция get_access() вы­звана с объектом и оператором «точка». При вызове статической функции может использовать­ся любая из этих форм и обе они дают одинаковый эффект. Стоит поэкспериментировать немно­го с этой программой, чтобы убедиться в правильности понимания хода ее работы.

Как отмечалось, статические функции имеют прямой доступ только к другим статическим фун­кциям или статическим данным в пределах одного и того же класса. Чтобы проверить это, по­пробуем откомпилировать следующую версию программы:

// данная программа содержит ошибку и не будет компилироваться
#include <iostream.h>
enum access_t {shared, in_use, locked, unlocked};
// класс контролирует редкий ресурс
class access {
static enum access_t acs;
int i; // не статический
// ...
public:
static void set_access (enum access_t a) {acs = a;}
static enum access_t get_access()
{
i = 100; // не будет компилироваться
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 out.\n";
// ...
}

Эта программа не будет откомпилирована, поскольку функция get_access() пытается получить доступ к нестатической переменной.

Сперва не ощущается необходимости в немедленном использовании статических членов, но по мере накопления опыта программирования на С++ они становятся очень удобным средством в определенных ситуациях, поскольку позволяют избежать использования глобальных переменных.