Переменные и операции класса

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

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

Пример. Опишем класс для работы с датами.

 

class Date{

int day;// день

int month;// месяц

int year;// год

public:

Date (int, int, int); // день, месяц, год

Date (int, int); // день, месяц, текущий год – по умолчанию

Date (int); // день, текущие месяц и год – по умолчанию

Date ( ); // дата по умолчанию – сегодня

. . .

};

 

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

Рассмотрим альтернативное решение. Опишем конструктор с аргументами по умолчанию.

 

class Date{

int day, month, year;

static Date default_date;

public:

Date (int d=0, int m=0, int y=0);

. . .

static void set_default(int, int, int);

};

 

Если значение аргумента является нулевым, следует воспользоваться соответствующим элементом даты, задаваемой по умолчанию – default_date. Поскольку значение default_date должно быть одинаковым для всех объектов класса, данная переменная является статическим членом. Для ее инициализации описана также статическая функция-член set_default. Статические члены – и функции, и данные – должны быть где-то определены. Например,

 

Date Date :: default_date (1, 1, 2000);

void set_default (int d, int m, int y)

{

Date :: default_date= Date (d, m, y);

}

 

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

 

Интерфейсы

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

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

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

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

 

 

Рис. 4.3. Интерфейс пользователя Рис. 4.4. Интерфейс телемастера

Группирование классов

 

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

Пакет не имеет операций или состояний в явном виде, они содержатся в нем не­явно в описаниях агрегированных классов.

Некоторые классы в пакете могут быть открытыми, то есть экспортиро­ваться для использования за пределы пакета. Остальные классы могут быть частью реализации, то есть не использоваться никакими классами, внешними к этому пакету.

В C++ пакеты классов реализуются с помощью введения пространств имен – namespace. Однако пространство имен позволяет реализовать более широкое понятие. Оно может включать в себя классы, другие пространства имен, свободные подпрограммы и глобальные (внутри пространства имен) данные.

Пример. Объединим все классы, разработанные для использования в графической системе в одну компоненту. Предоставим пользователю описания в файле GraphSys.h:

 

namespace GraphSys{

class Point{ ... };

class Color{ ... };

class Shape {... };

class Circle: public Shape{... };

class Triangle: public Shape {... };

class Square: public Shape{... };

class SolidCircle: public Circle {... };

}

 

Реализация указанных классов находится в файле GraphSys.срр:

 

namespace GraphSys{

Circle :: draw ( ){...}

Triangle :: draw ( ){...}

Square :: draw ( ){...}

SolidCircle :: draw ( ){...}

. . .

}

 

Обращение к членам пространства имен осуществляется с использованием явной квалификации:

 

GraphSys :: Circle C;

GraphSys :: SolidCircle SC;

 

С другой стороны, описание using в пользовательском коде позволяет не использовать все время явную квалификацию:

 

#include GraphSys.h

using namespace GraphSys;

void user_func ( ){

Circle C;

. . .

C -> draw ( );

}