Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Штерн В. - Основы C++. Методы программной инженерии - 2003

.pdf
Скачиваний:
267
Добавлен:
13.08.2013
Размер:
28.32 Mб
Скачать
// совместно с другими фигурами // вес единицы объема многоугольника

510

Часть Hi ^ Програтттро&ание с агрегированием и наследованием

 

то их совместное использование с другими объектами Rectangle не дает никаких

 

преимуществ. Их можно сделать компонентами-константами.

 

class Rectangle {

const

Point& pt1;

/ /

точки не используются

 

 

/ /

совместно с другими фигурами

const

Point& pt2;

/ /

точки не могут изменять свои координаты

 

 

/ /

остальная часть класса Rectangle

Ссылку на объект-константу можно применять как метод оптимизации. Если большое количество составных объектов должно содержать один и тот же объекткомпонент, имеет смысл создать только один такой объект, а во всех составных объектах использовать ссылки на данный объект.

Процесс построения объекта класса Rectangle представлен на рис. 12.5. Сна­ чала создаются объекты типа Point (см. рис. 12.5(A)), затем — объект Rectangle (см. рис. 12.5(B)): задаются ссылки pt1 и pt2. Их тип отличается от типа в преды­ дущем примере. Используются другие размеры фигур, чтобы показать разницу типов. Выполняется список инициализации, и ссылки устанавливаются на объек­ ты Point, создается и инициализируется поле-константа weight, а затем — файл thickness. Вызывается конструктор Rectangle (см. рис. 12.5(C)). Присваивается значение поля thickness.

А)

 

 

Point p1 (70,90);

В)

 

 

a) Выделение памяти для ссылки pt1

 

 

b) Ссылка на объект р1: pt1(p1)

 

 

 

c) Выделение памяти для pt2

 

 

 

d) Ссылка на объект р2: pt2(p2)

 

 

 

e) Создание weight

 

 

 

f) Инициализация: weight(wt)

 

 

 

g) Создание thickness

С)

pt1

 

Выполнение

 

Pt2

 

конструктора Rectangle

 

weight

0.01

 

 

ickness

4 1

thickness = weight;

Рис. 12.5. Этапы

создания

объекта Rectangle со ссылками

на внегиние объектны Point

В данной архитектуре ссылки в классе Rectangle представляют собой константы, но объекты класса Point, на которые они указывают, не являются константами. Следовательно, они могут менять свое состояние. Все объекты Rectangle, ассо­ циированные с этими объектами Point, могут изменять свое положение на экране. За счет ссылок объект Rectangle не может потерять связанный с ним объект Point и использовать вместо него другой объект Point. Это возможно, если класс Rectangle вместо ссылок применяет указатели.

class Rectangle { Point *pt1, *pt2; int thickness; const int weight;

public:

Rectangle (const Point*, const Point*, int = 1, int = 1) void move(int a, intb);

Глава 12 • Преимущества и недостатки составных классов

511

void

setThickness(int w=1);

 

 

 

 

int

pointIn(const

Point

&pt)

const;

 

 

 

. . . . } ;

 

 

 

/ /

остальная

часть класса

Rectangle

Rectangle::Rectangle(const

Point

*p1, const

Point *p2,

 

int width,

int wt)

: pt1(p1),

pt2(p2), weight(wt)

/ / тоже не обязательно

{ thickness

= width;

}

 

 

/ / тот же конструктор,

что и выше

Так как указатели могут изменяться в любой момент своего существования, применение списка инициализации здесь не обязательно, но это хорошая практика.

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

class Rectangle {

 

 

const

Point

*pt1;

/ /

точки могут использоваться

 

 

 

/ /

совместно с другими фигурами

const

Point

*pt2;

/ /

точки не могут изменять свои координаты

. . . .

} ;

 

/ /

остальная часть класса Rectangle

Такая конструкция может дать полезную оптимизацию, если большое число объектов Rectangle ассоциировано с одними и теми же объектами Point.

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

Использование объектов как элементов данных своего собственного класса

в предыдупдих разделах обсуждалась ситуация, когда объект одного класса (например. Point) использовался как элемент данных другого класса (например. Rectangle). Может ли объект класса быть компонентом своего собственного класса? Например, приложению могут потребоваться координаты точек, относя­ щихся к определенным зонам экрана, и спецификация точки привязки как харак­ теристики класса Point.

class Point {

 

 

 

int X, y;

/ /

закрытые координаты

Point anchor

/ /

это не допускается

public:

 

 

 

Point (int a--=0, int b=--0)

/ /

многосторонний

конструктор

{ X = a; У = b; }

/ /

остальная часть

класса Point

. . . . } ;

Между тем это не допускается. В начале главы уже Рассматривалась последова­ тельность событий при распределении памяти для объекта. Память для элемен­ тов данных выделяется в порядке их определения в спецификации класса. (Для статических компонентов — в начале выполнения программы.) Таким образом, при создании объекта Point сначала выделяется память для х и у, а затем — для anchor ("якоря", точки привязки). Однако anchor имеет тип Point, поэтому вновь определяется память для его компонентов х, у, а затем anchor и т. д. Такой ре­ курсивный процесс не заканчивается, поэтому он запрещен.

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

512

Часть III * Прогром!мирование с огрегировани01\^ и наследованием

 

память для них выделяется наряду с другими элементами данных без дополнитель­

 

ных трудностей.

 

class Point {

int X,

у;

 

/ /

закрытые

координаты

Point

&anchor;

/ /

это допускается

public:

 

 

 

 

 

Point

(int

а=0, int

b=0, Point &focus)

 

: anchor

(focus)

/ /

не может устанавливаться в конструкторе

{ X = а; у = Ь;

}

 

 

. . . }

;

 

/ /

остальная

часть класса Point

Как уже говорилось в предыдущем разделе, ссылочные элементы данных не могут инициализироваться в теле конструктора класса, поэтому здесь они инициа­ лизируются в списке с помощью объекта класса Point, передаваемого конструк­ тору в виде аргумента. Параметр конструктора не имеет модификатора const, потому что элемент данных anchor не определен как константа. Через ссылку anchor объект Point может модифицировать объект, переданный как аргумент. Вот почему определение параметра как константы было бы синтаксической ошибкой.

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

class Point {

 

 

 

 

 

int X,

у;

 

 

 

/ /

закрытые координаты

Point

&anchor;

 

 

/ /

это допускается

public:

 

 

 

 

 

 

Point

(Point &focus,

int a=0, int

b=0)

/ /

правильный порядок

: anchor

(focus)

/ /

не может устанавливаться в конструкторе

{ X = а;

у = Ь; }

 

 

 

 

. . . } ;

 

 

 

 

/ /

остальная часть класса Point

Клиент сначала создает базовую точку (anchor), а затем передает ее в аргументе конструктору новых точек. Первая точка должна быть базовой.

Point р1(р1); / / синтаксическая ошибка: р1 не определяется как аргумент

Это синтаксическая ошибка. Когда р1 используется в аргументе конструктора, данный объект создается. Следовательно, ссылка на него пока не определена. Поэтому память для базовой точки выделяется динамически.

Point

*р = new Point(*p,80,90);

/ /

р еще не имеет значения

Point

р1(*р);

/ /

*р используется как базовая точка

Здесь возникает проблема: при использовании р как аргумента конструктора данная переменная еще не имеет значения, однако это приводит не к синтаксиче­ ской ошибке, а к предупреждению. Избежать предупреждения можно путем ини­ циализации указателя с помощью нулевого значения перед его использованием.

Point

*р = 0;

/ /

важно избежать предупреждения

р = new Point(*p,80,90);

/ /

динамическое

распределение объекта Point

Point

р1(*р);

/ /

используется

как базовая точка

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

Глава 12 • Преимущества и недостатки составных классов

| 513 |

Использование статических элементов данных как компонентов собственного класса

Использование статических элементов данных как компонентов собственного класса допускается, и это намного проще, чем применение ссылочных элементов данных. Например, объекты класса Point могут иметь общую для всех точек плос­ кости точку начала координат. Такая точка является общей, поэтому ее можно представить как статический элемент данных.

class Point {

 

 

 

 

 

int X,

у;

 

 

 

/ /

закрытые координаты

static

int

count;

 

 

 

static

Point

origin;

/ /

статический объект, OK

public:

 

 

 

 

 

 

Point

(int

a,

int

b)

 

 

{ X = a;

у = b;

count++;

}

 

Синтаксис инициализации статического объекта такой же, как для статических элементов данных встроенных типов. (Подробнее о статических элементах данных рассказывается в главе 9.) Следующая строка показывает пример определения и инициализации статического объекта. Первое Point здесь представляет тип определяемых элементов данных (origin). Второе показывает, что определяемый элемент данных принадлежит к классу Point. При создании объекта для инициали­ зации его полей вызывается конструктор. В данном случае это обобщенный конст­ руктор Point с двумя параметрами.

Point Point::origin(640,0);

/ / инициализация с помощью конструктора

Это аналогично определению статических элементов данных встроенных типов, например count. Данное поле имеет целый тип, и операция области действия по­ казывает, что оно принадлежит к классу Point. Начальное значение поля устанав­ ливается в нуль.

int Point::count = 0;

Подобно другим статическим объектам, не вполне понятно, когда именно создает­ ся объект и вызывается конструктор. Если в программе несколько статических переменных, то порядок их создания не определен. Расположение их в опреде­ ленном порядке в исходном коде еще не гарантирует, что они будут создаваться и инициализироваться именно так. Отмечается лишь то, что они будут созданы перед выполнением первого оператора функции main().

В случае класса Point такой гарантии недостаточно. Конструктор Point вызы­ вается как для нестатических объектов Point, так и для статического объекта origin, создаваемого первым. При этом статический элемент данных count дол­ жен быть создан перед объектом origin.

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

class

Point

{

 

 

 

 

int

X,

у;

 

 

 

 

 

static

int

count;

 

 

 

static

Point

origin;

 

/ /

статический объект, OK

Point

&anchor;

 

/ /

ссылка или указатель, OK

public:

 

 

 

 

 

 

Point

(Point

&focus

= origin,

int a=0, int

b=0) : anchor(focus)

 

{ X = a;

у = b;

count++;

}

 

514

Часть III« Программирование с агрегированием и наследованием

 

void set (int а = х, int b)

/ /

ошибка, нестатический

элемент данных

 

{ X = а; у = b; }

/ /

остальная часть класса

Point

 

 

Статические элементы данных создаются до начала выполнения программы. Доступ к ним возможен до создания объектов классов.

После создания объектов Point элементы данных count и origin могут быть доступны с помондью любого объекта. Результат будет одним и тем же, поскольку они статические. В отличие от нестатических элементов данных к ним нельзя обращаться с помощью имени класса, а не имени целевого объекта.

int mainO

 

 

 

 

{ Point р1, р2(70.90);

«

р1.count «

endl;

// выводит 2

cout «

"Число точек:

cout «

"Число точек:

«

р2.count «

endl;

// выводит 2

. . .

}

 

 

 

 

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

int mainO

 

{Point р1, р2(70,90);

 

cout « "Число точек: " « Point::count « endl;

/ / также выводит 2!

. . . }

 

Более того, данный синтаксис доступен, даже когда объекты классов еще не со­ зданы.

int mainO

//выводит О

{ cout « "Число точек: " « Point::count « endl;

Листинг 12.3 показывает вариант класса Point с двумя статическими элемен­ тами данных — count и origin. Их можно инициализировать вне определения

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

Листинг 12.3. Использование статических элементов данных истатической функции-члена

#include <iostream> using namespace std;

class Point {

 

// закрытые координаты

int X, y;

 

static int count;

 

static Point origin;

 

public:

 

// обобщенный конструктор

Point (int a=0, int b=0)

{ X = a; у = b; count++;

" y=" « у

cout «

Создан: )(=" « x «

«

" count=" « count « endl; }

static int quantityO

// const недопускается

{ return count; }

// функция-модификатор

void set (int a, int b)

{ X = a; у = b; }

// функция-селектор

void get (int& a, int& b) const

{ a = x; b = y; }

 

Глава 12 • Преимущества и недостатки составных классов

515

void move (int а, int b)

// функция-модификатор

 

{ X += a; у += b; }

 

// деструктор

 

"'PointO

 

 

{ count-;

 

"y=" « y «endl; }

 

cout « " Объект Point уничтожен: x=" « x «

 

} ;

 

// инициализация

 

int Point::count = 0;

 

 

Point Point::origin(640,0);

 

// инициализация

 

int mainO

Point::quantity() «

endl;

 

{ cout « " Число точек: " «

 

Point p1, p2(30), p3(50,70);

// точка начала координат, объекты точек

cout « " Число точек: " «

р1 .quantityO « endl;

 

return 0;

 

 

 

}

 

 

 

Создан: х=640, у=0, count=1 Число точек: 1

Создан: х=0, у=0, count=2 Создан: х=30, у=0, count=3 Создан: х=50, у=70, count=4 Число точек: 4

Объект Point уничтожен: х=50 у=70 Объект Point уничтожен: х=30 у=0 Объект Point уничтожен: х=0 у=0

Рис. 12.6. Вывод программы из листинга 12.3

Результат данной программы представлен на рис. 12.6. Как видно, переменная count не равна нулю, хотя экземп­ ляры объектов Point явно не создавались. Это подсчитан статический объект origin. Данный объект создан до вы­ полнения первого оператора main(). Когда вызывается его конструктор, появляется отладочное сообщение "Создан" (первое в выводе). Этот статический элемент данных унич­ тожается после завершения программы, поэтому сооби;ение об исчезновении так и не появляется.

Если поменять определения переменных count и origin, результат программы не изменится.

Point Point::origin(640,0); int Point::count = 0;

Это означает, что компилятор может отслеживать зависимость между двумя данными переменными и обеспечивать доступность переменной count во время выполнения конструктора Point для статического объекта origin.

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

Контейнерные классы

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

Даже в первых примерах составных классов, обсуждавшихся в этой главе (таких как класс Rectangle), содержалась совокупность компонентов (в данном случае — объектов класса Point), однако она не была динамической. Число объектов Point, связанных с объектом Rectangle, всегда одинаково и равно двум. Если бы два объекта Point были недоступны в клиенте, невозможно было бы создать объект Rectangle.

516 Часть III« Прогро^^ирование с arpernpOBOHnetvi и насдеАОвание!^

Часто в приложении необходим класс-контейнер или класс-набор, содержащий переменное число объектов. Обычно контейнерный объект пуст и не содержит компоненты. При выполнении приложения объекты-компоненты становятся до­ ступными и добавляются в контейнер для временного хранения или для обработки.

Например, контейнер может представлять собой предъявляемый к оплате счет заказчика, а компоненты — под/1ежащие обработке операции с кредитной картой заказчика. Обработка может включать в себя вычисление общей суммы, налогов, вывод отчетов и выполнение других задач. Кроме того, можно проверить наличие в контейнере искомых данных, удалить компоненты из контейнера и обеспечить повторное использование контейнера.

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

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

Вданном разделе приводится ряд примеров простых контейнеров. Показано, как их можно использовать в клиенте.

Впримерах применяются компоненты класса Sample, содержащего всего один элемент данных value типа double. Объекты Sample генерируются внешним про­ цессом. Это могут быть котировки акций, данные устройства мониторинга, зна­

чения температуры и давления и т. д. В примерах значения берутся из массива с заранее заданными элементами.

class

Sample

{

 

 

double value;

 

 

public:

 

 

 

Sample (double x = 0)

{

value

= x;

}

 

void

set (double

x)

{

value

= x;

}

 

double show ()

const

{

return

value;

}. }

/ /

класс компонента

/ /

указателей среди элементов данных нет

/ /

конструкторы: по умолчанию/преобразования

// модификатор

// селектор

Когда класс используется как компонент контейнера, он должен отвечать тре­ бованиям архитектуры. Наиболее распространенные требования — это способ­ ность поддерживать:

Создание экземпляров по умолчанию

Присваивание

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

Sample data[100];

/ / компонент данных контейнера

Глава 12 • Преимущества и недостатки составных классов

| 517 |

Еще одна популярная архитектура контейнерного класса — использование ди­ намически распределяемого массива компонентов.

Sample

*clata;

/ /

элемент данных контейнера

data =

new SaiTiple[100];

/ /

код в конструкторе контейнера

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

В конечном счете попытка создания составного объекта без конструктора по умолчанию класса компонента приводит к синтаксической ошибке. Ее можно устра­ нить, включив в конструктор контейнера список инициализации компонентов. Этот метод работает, только когда число компонентов в контейнере известно заранее и не очень велико. Однако в списке инициализации компонентов нужно перечислять каждый компонент составного класса, явно указывая имя компонента.

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

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

Этот метод хорошо использовать для простых классов вида Sample, поскольку в них не применяется динамическая память. Если же класс компонента содержит указатели и работает с динамически распределяемой памятью, может потребо­ ваться перегруженная операция присваивания.

Еш.е один метод поддержки присваивания состоит в реализации для класса компонента функции-модификатора, которая будет изменять состояние объектакомпонента.

data[i] . set(s); / / код в методе контейнера

Класс Sample поддерживает это требование, предусматривая метод set() и допус­ кая прямое присваивание без использования перегруженной операции присваи­ вания.

Часто предполагается, что класс компонента должен отвечать еш,е двум требо­ ваниям, т. е. поддерживать:

Создание копий экземпляров объектов

Семантику обндего порядка

Создание копий экземпляров — это способность создавать экземпляр объектакомпонента из другого объекта-компонента. Это свойство поддерживается с помош,ью реализации в классе компонента конструктора копирования и необходимо, если контейнерный класс должен возврандать копию одного из своих объектовкомпонентов клиенту. Часто клиента устраивает не полноценная копия объектовкомпонентов, а ссылка на них. Создание экземпляров-копий в алгоритмах кон­ тейнеров происходит редко. Его поддержка неизбежно поош,ряет передачу пара­ метров-объектов по значению или возврат значений-объектов из функций со всеми негативными последствиями, поэтому лучше не включать в класс конструк­ тор копирования и надеяться на лучшее. Вы столкнетесь с проблемами, особенно, если класс работает с динамически распределяемой памятью. Если не допускается передача объектов по значению или возврат значений-объектов, лучше применять частные конструкторы (см. главу 11).

^ЯР

518Часть III • Программирование с агрегированием и наследованием

Со в е т у е м не торопитесь снабжать каждый класс конструктором копирования.

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

Таким образом вы сможете предотвратить появление проблем.

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

История

измерений:

В следующих примерах объекты класса Sample хранятся

в контейнерном объекте класса History. Класс History хранит

3 5

7 11 13 17 19 23

объект Sample в коротком массиве (для простоты в нем всего

Среднее

значение: 12.25

лишь восемь элементов). Это позволяет клиенту устанавливать

 

 

значение Sample в заданном месте массива, выводить набор из-

10

7

мерений и вычислять среднее для измеренных значений.

Результат выполнения

Листинг 12.4 показывает первую версию контейнерного клас-

wpozpaMMbi из листинга 12А

са. Результат программы представлен на рис. 12.7.

Листинг 12.4. Контейнерный класс с массивом компонентов фиксированного размера

#inclucle <iostream> using namespace std;

class Sample {

 

 

// класс компонента

double value;

 

 

// значение для примера

public:

 

 

 

// конструктор: поумолчанию и преобразования

Sample (double x = 0)

{ value = x; }

 

 

// метод-модификатор

void set (double x)

 

{ value = x; }

 

 

// метод-селектор

double get () const

 

{ return value; } } ;

 

class History {

 

 

// контейнерный класс

enum {size = 8 };

 

// массив значений (фиксированного размера)

Sample data[size];

 

public:

 

 

 

// модификация значения

void set(double,int);

void print 0 const;

// вывод предыстории

void averageO const;

// вывод среднего значения

} ;

 

 

 

 

void History::set(double s, int i)

// или просто: data[i] = s;

{ data[i].set(s); }

 

void History::print

() const

// вывод предыстории

{ cout «

"\n История измерений:" « endl «

endl;

for (int i = 0; i < size; i++)

// локальный индекс

cout « " " «

data[i].get(); }

 

void History::average

() const

// вывод среднего значения

{ cout «

"\n Среднее значение:

double sum = 0;

 

 

// локальное значение

for (int i = 0; i < size; i++)

// локальный индекс

cout «

sum +=data[i].get();

 

sum/size «

endl; }

 

 

 

Глава 12 • Преимущество и недостатки составнь1х классов

519

i nt

mainO

 

 

 

 

 

 

{

double

а[] = {3,

5,

7, 11, 13. 17, 19, 23, 29 } ;

/ /

исходные данные

 

History

h;

 

 

/ /

конструктор по умолчанию

 

for (int i=0; i

< 9;

i++)

/ /

доступно

8 слотов

 

 

h . s e t ( a [ i ] , i ) ;

 

/ /

установка

предыстории

 

h . printO;

 

 

/ /

вывод предыстории

 

h.averageO;

 

,

//вычисление среднего значения

return

0;

 

 

 

 

 

 

}

 

 

 

 

 

 

 

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

Как правило, у разработчика класса есть выбор, позволяющий ему сделать значение или переменную доступной методу класса для хранения или получения значения:

Глобальная переменная или общедоступный элемент данных

Параметр метода

Элементы данных класса

Локальная переменная в методе

Глобальная переменная может использоваться, когда несколько классов долж­ ны совместно работать с информацией, но разработчик затрудняется определить, к какому из них относится подобное сообщение. Такой метод коммуникаций между классами представляет наивысшую степень связности, и применять его следует по возможности реже. Общедоступные элементы данных можно использовать, когда разработчик выбирает для хранения информации конкретный класс, но эта информация нужна и другим классам. Она будет доступна им в форме элемента данных public. Такая степень связи столь же высока, как и при использовании глобальной переменной, и не следует применять ее часто.

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

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

Коммуникации через параметры метода следует применять в том случае, если значение или переменная будут совместно использоваться классом и его клиен­ том. Например, параметры метода History: :set() совместно применяются клиен­ том main() и функцией-членом set() класса History.

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

Соседние файлы в предмете Программирование на C++