Интерфейсы

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

Обычно для каждого объекта необходима своя копия переменных, описанных в классе. Однако в некоторых ситуациях требуется, чтобы в классе были данные, общие для всех его экземпляров – переменные класса.

Переменная класса (статическая переменная) в С++ описывается с ключевым словом static, она создается один раз как часть класса, а не для каждого конкретного экземпляра данного класса. Функция, которой требуется доступ к статическим переменным, но не требуется, чтобы она вызывалась для конкретного экземпляра класса, также описывается как статическая (static).

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

Статические переменные должны быть дополнительно описаны и инициализированы вне определения класса как глобальные переменные (даже если первоначально они описаны в закрытой или защищенной части класса), после инициализации к ним можно обращаться даже до определения объектов данного класса.

При наследовании статические атрибуты наследуются, но это атрибуты суперкласса, единые для всех подклассов.

Пример. Используем статическую переменную для подсчета объектов-фигур, потомков класса Shape.

class Shape {

static int count; // счетчик фигур – статическая переменная

...

public:

Shape() {count++;}

~ Shape() {count--;}

static int get_count() {return count;} // статическая get-функция

...

};

int Shape:: count = 0; // описание и инициализация вне класса

...

int c; Circle C; Triangle T; SolidCircle SC;

c = C.get_count(); // три эквивалентных

c = Shape:: get_count(); // обращения к

c = Circle:: get_count(); // статической функции

Хотя класс Shape и является абстрактным, мы описали в нем конструктор и деструктор, цель которых состоит лишь в изменении статической переменной – счетчика. При объявлении объекта подкласса будет вызван конструктор класса Shape, а при уничтожении – деструктор. По этой причине изменять счетчик в конструкторе и деструкторе производных классов не нужно.

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

Задача. Что напечатает программа?

class Point {public: static int count;...};

int Point:: count=0;

void main() {

Point P1, P2;

P1.count =1; P2.count =2; printf("%d\n", P2.count);

P1.count++; printf("%d\n", P1.count);

}

Когда с помощью объектно-ориентированного подхода начали разрабатывать крупные программные системы, выяснилось, что кроме классов нужны дополнительные уровни абстракции. В частности, если сложный объект имеет разное поведение (играет разные роли), в зависимости от того, с кем он взаимодействует, то бывает удобно скрыть все функции, не нужные в данный момент. А точнее: на время данного взаимодействия сделать доступными все необходимые функции и только их. Для описания таких групп функций удобно использовать понятие интерфейса. В данном контексте интерфейс удобно рассматривать как абстрактный класс, содержащий только чисто виртуальные функции и не имеющий собственных данных.

Пример. Все элементы управления телевизором можно разделить на несколько групп: пользовательские (громкость, номер канала), специальные (частота канала) и аппаратные (параметры электрических цепей). При этом пользователь работает с пользовательскими органами управления, настройщик – со специальными, а телемастер – с аппаратными. При этом, если телевизор исправен и настроен, пользователю нет необходимости видеть и менять состояние специальных и аппаратных органов управления. Поэтому пользовательские элементы управления обычно выносятся на переднюю панель телевизора, специальные закрыты небольшой дверцей, а аппаратные вообще погружены внутрь корпуса. Если бы все было на поверхности, пользователь мог бы сделать все то же, что и раньше, но для него оказались бы доступными специальные и аппаратные органы управления, и он мог бы случайно испортить настройки. Кроме того, передняя панель была бы загромождена настолько, что мало кто смог бы ориентироваться в обилии кнопок, ручек и т.п.

Между интерфейсами могут существовать отношения наследования, ассоциации и зависимости, аналогичные одноименным отношениям между классами. Между классом и интерфейсом могут существовать отношения реализации и зависимости.Будем говорить, что класс реализует (или поддерживает)интерфейс, если он содержит методы, реализующие все операции интерфейса. Интерфейс может реализовываться несколькими классами (например, пользовательские элементы радиоприемника и телевизора совпадают), а класс может реализовывать несколько интерфейсов (например, телевизор реализует три интерфейса). С другой стороны, класс может зависеть от нескольких интерфейсов, при этом предполагается, что какие-то классы эти интерфейсы реализуют.

# define interface struct // функции – элементы интерфейса – открыты

interface IUser{ // пользовательский интерфейс

virtual void change_channel(int)=0;

virtual void change_sound(int)=0;

};

class Televisor: public IUser, public ISpecial, public IApparatus {

// класс Televisor реализует пользовательский, специальный и

// аппаратный интерфейсы

int channel, sound_level;

...

public:

virtual void change_channel(int number) {channel = number;}

virtual void change_sound(int rel) {sound += rel;}

...

};

class User { // класс User зависит от интерфейса IUser

public: User(IUser *IU);

...

};

...

Televisor* My_TV;...

User My(My_TV);

На рис. 4.3 показано, что пользователь взаимодействует с телевизором посредством интерфейса IUser, а телемастер – посредством интерфейса IApparatus.

Рис. 4.3. Интерфейсы


Понравилась статья? Добавь ее в закладку (CTRL+D) и не забудь поделиться с друзьями:  



double arrow
Сейчас читают про: