Программирование >>  Составные структуры данных 

1 ... 37 38 39 [ 40 ] 41 42 43 ... 225


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

Этот небольшой класс рассматривается в качестве примера с той целью, чтобы познакомиться с основными особенностями классов; поэтому он - далеко не полностью завершенный класс. В реальном коде для класса точки у нас будет намного больше операций. Например, в программе 4.1 отсутствуют даже операции, позволяющие узнавать значения координат х и у. Как мы увидим, добавление этих и других операций - задача довольно простая, В части 5 мы более подробно рассмотрим классы для точки и других геометрических абстракций, например, линий и многоугольников.

В языке С++ (но не в С) у структур также могут быть связанные с ними функции. Ключевое различие между классами и структурами связано с доступом к информации, который характеризуется ключевыми словами private и public. На приватный (private) член класса можно ссылаться только внутри класса, в то время как на оби(е-доступный (public) член класса может ссылаться любой клиент. Приватными членами могут быть как данные, так и функции: в программе 4.1 приватными являются только данные, но далее мы увидим многочисленные примеры классов, в которых приватными будут также функции-члены. По умолчанию члены классов являются приватными, тогда как члены структур - общедоступными.

Например, в клиентской программе, использующей класс POINT, мы не можем ссылаться на данные-члены р.х, р.у и т.д., как могли бы в случае, если бы программа использовала структуру POINT, поскольку члены класса х и у являются приватными. Все что можно предпринять - это использовать для обработки точек общедоступные функции-члены. Такие функции имеют прямой доступ к данным-членам любого объекта этого класса. Например, когда в программе 4.1 мы вызываем функцию distance с помощью оператора p.distance(q), в операторе dx = х - а.х имя х относится к данным-членам х в точке р (поскольку функция distance была вызвана как функция-член экземпляра р), а имя а.х относится к данным-члену х в точке q (так как q - это действительный параметр, соответствующий формальному параметру а). Дабы исключить возможную двусмысленность или путаницу, можно было бы записать dx = this->x-a.x, - ключевое слово this относится к указателю на объект, для которого вызывается функция-член.

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



ся объектов: можно в класс POINT включить переменную static int N, добавить в конструктор N++, и тогда появится возможность знать количество созданных точек.

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

static float distance(POINT a, POINT b) {

float dx = a.x - b.x, dy = a.у - b.y; return sqrt(dx*dx + dy*dy);

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

Другая возможная альтернатива - определить функцию distance как независимую функцию вне объявления класса POINT (используя тот же самый код, что и в предыдущем абзаце, но опуская ключевое слово static). Поскольку эта версия функции distance должна иметь доступ к приватным данным-членам класса POINT, в объявление класса POINT потребуется включить строку

friend float distance(POINT, POINT);

Дружественная (friend) функция - это функция, которая, не будучи членом класса, имеет доступ к его приватным членам.

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

float Х() const { return х; } float Y() const { return у; }

Эти функции определяются с ключевым словом const, так как они не модифицируют данные-члены объекта, для которого вызываются. Мы часто включаем в классы языка С++ функции такого типа. Обратите внимание, что если бы использовалось другое представление данных, например, полярные координаты, то реализовать эти функции было бы труднее; тем не менее, эта трудность была бы прозрачной для клиента. Преимущества подобной гибкости можно также задействовать в функциях-членах - если в реализации функции distance из программы 4.1 вместо ссылок типа а.х использовать ссылки типа а.Х(), то при изменении представления данных не придется изменять этот код. Помещая эти функции в приватную часть класса, можно позволить себе такую гибкость даже в тех классах, где не требуется доступ клиента к данным.

Во многих приложениях главная цель создания класса связана с определением нового типа данных, наиболее адекватно отвечающего потребностям приложения. В таких ситуациях часто обнаруживается, что использовать этот тип данных требуется таким же самым образом, как и встроенные типы данных языка С++, например, int или float. Эта тема рассматривается более подробно в разделе 4.8. Один важный инструмент, помогающий нам достичь этой цели, называется перегрузкой операций (operator overloading): в языке С++ она позволяет задать, что к объекту класса необхо-



димо применить фундаментальную операцию и что в точности эта операция должна делать. Например, предположим, что требуется считать две точки идентичными, если расстояние между ними меньше чем 0.001. Добавляя в класс код

friend int operator==(POINT a, POINT b) { return distance (a, b) < .001; }

можно С помощью операции == проверять, равны ли две точки (согласно приведенному определению).

Другой операцией, которую обычно желают перегрузить, является операция из класса ostream. При программировании на языке С++ обычно полагают, что эту операцию можно использовать для вывода значений любого объекта; в действительности же так можно делать только в том случае, если в классе определена перегруженная операция .В классе POINT это можно сделать следующим образом:

ostream& operator (ostreamfi t, POINT p) {

cout ( p.X() , P.Y() ) ; return t;

Эта операция не является ни функцией-членом, ни даже дружественной: для доступа к данным она использует общедоступные функции-члены Х() и Y().

Как понятие класса языка С++ соотносится с моделью клиент-интерфейс-реализация и абстрактными типами данных? Оно обеспечивает непосредственную языковую поддержку, но достаточно распространенным является тот факт, что существует несколько различных подходов к созданию класса, которые можно использовать. Общепринято следующее правило: объявления общедоступных функций в классе образуют его интерфейс. Другими словами, представление данных хранится в приватной части класса, где оно недоступно для программ, использующих класс (клиентских программ). Все, что клиентские программы знают о классе - это общедоступная информация о его функциях-членах (имя, тип возвращаемого значения и типы аргументов). Чтобы подчеркнуть природу интерфейса (т.е. то, что он определяется посредством класса), сначала рассмотрим интерфейс (как это иллюстрируется в программе 4.3). Затем мы рассмотрим одну реализацию - программу 4.1. Здесь важно то, что такой порядок упрощает исследование других реализаций, с другими представлениями данных и другими реализациями функций, а также тестирование и сравнение реализаций, позволяя делать это без каких-либо изменений клиентских программ.

Программа 4.3 Интерфейс абстрактного типа данных POINT

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

class POINT

private:

прохаммный код, эависвщий от реализации



1 ... 37 38 39 [ 40 ] 41 42 43 ... 225

© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки.
Яндекс.Метрика