36
здавать объектно-ориентированные программные системы, называются «расширением языка Си». При этом необходимо отметить, что новые возможности в языке С++ отнюдь не делают написанные на нём программы объектноориентированными сами по себе. Язык С++ позволяет создавать процедурноориентированные программы с таким же успехом, как и не объектноориентированный язык Си. Для создания объектного кода необходимо понимать, что язык С++ является лишь более подходящим средством для реализации объ- ектно-ориентированной парадигмы.
Таким образом, для осуществления прямых операций над сложными, отражающими сущности реального мира структурами данных в объектноориентированной технологии имеют место высокоуровневые, типонезависимые методы обработки данных. Примером тому может служить разработка специальных операций сложения, вычитания, произведения и т.д., для матриц, векторов, узлов списков и прочих структур данных. Технология перегрузки операторов относится к одному из разделов «полиморфизма» (общий полиморфизм).
Пример перегрузки базовых операторов «+» и «*» для случая абстрактных типов данных в качестве операндов (написан на псевдо-языке программирования) приведён на рисунке 6.
В языке программирования С++ существуют ограничения на перегрузку операторов. С одной стороны такие ограничения связаны с поддержкой семантики операций (унарность и бинарность), с другой стороны, ограничения на перегружаемые операции поддерживают целостность языка. Например, запрещена перегрузка операторов с изменением приоритетов исполнения.
Тип_результата_сложения Функция_перегрузки_знака+(слагаемое_матрица1, слагаемое_матрица2){описание способа сложения матриц 1 и 2 и возврат результата}
*************************************************************
Тип_результата_умножения Функция_перегрузки_знака* (множитель_число_А, множитель _матрица_В){описание способа перемножения числа А и матрицы_В и возврат результата}
Рисунок 6 – Пример перегрузки базовых операторов «+» и «*» для случая абстрактных типов данных в качестве операндов
37
Пример перегрузки базового оператора «+» на языке С++ для случая сложения комплексных чисел приведён на рисунке 7. В данном примере следует обратить внимание на типы формальных параметров: complex a и complex b функции перегрузки operator+(). Именно этот факт является определяющим для «контекста» вызова данной функции, что однозначно направит развитие событий по сценарию, описанному внутри функции перегрузки операции «+» :
complex operator+(complex a,complex b).
Втом случае, если операндами оператора «+» будут являться переменные базового типа (например int), то контекст вызова оператора перегрузки будет иным, и, следовательно, развитие событий пойдёт по стандартному сценарию, описанному для базовых типов данных.
Воснове технологии перегрузки операций и функций лежит раннее статическое связывание, выполняемое на этапе компиляции. Это позволяет вызывать тот, необходимый из многих кодов реализации некоторой операции или функции, который определяется пространством имён и контекстом при обращении к рассматриваемой функции (например, типом параметров при вызове функции).
Следует помнить, что процесс компиляции исходного кода не предназначен для отслеживания различных предполагаемых правил (например, правил коммутативности сложения при перегрузке оператора «+», правил возведения в степень при перегрузке оператора «**» и т.д). Ответственность за результаты исполнения перегруженных операций несёт сам программист. На этапе компиляции также будет выполнена проверка попыток перегрузки функций, связанных с новыми лексическими символами, в случае обнаружения таких переопределений компилятор выдаст сообщение об ошибке. Очень часто неопытные, начинающие «с нуля», программисты допускают «тактические» ошибки, которые заключаются в «передоверии» компилятору контроля правильности абсолютно всех операций программы. При таком «подходе» результаты выполнения программы могут быть самыми неожиданными, даже если на этапе компиляции не было получено никаких сообщений об ошибках.
|
|
|
38 |
|
|
||
typedef struct complex {float re,im}; |
//объявление состава структуры |
||
|
|
|
//для комплексного числа |
complex operator+(complex a,complex b) //интерфейс функции пере- |
|||
|
|
|
//грузки оператора «+» |
{ a.re=a.re + b.re; |
//описание правил сложения |
||
a.im=a.im + b.im; |
//комплексных чисел |
||
return a; |
} |
//по элементам |
|
//----------------------- |
|
реализация в программе ------------------------------ |
|
void main() |
|
|
|
{ complex a,b; |
//объявление комплексных переменных |
||
a.re=1.0; |
|
//инициализация комплексных переменных |
|
a.im=-1.0; |
|
|
|
b.re=2.0; |
|
|
|
b.im=1.0; |
|
|
|
a=a+b;} |
//выполнение сложения при котором выполняется |
||
//перегрузка оператора «+» в контексте комплексных чисел, //т.е. при наличии формальных параметров а и в //имеющих тип «структура complex»
Рисунок 7 – Пример перегрузки базового оператора «+» для случая сложения комплексных чисел
Кратко сформулировать принцип раннего статического связывания можно следующим образом: некоторые функции могут иметь одинаковые наименования, но различное назначение и, следовательно, реализацию. При вызове такие функции отличаются одна от другой посредством «сигнатур», т.е. качеством (типом), количеством, и порядком следования формальных параметров в вызываемой функции. При компиляции исходного кода каждой такой функции присваивается новое мнемоническое наименование, что и позволяет реализовать вызов одной определённой и необходимой в данный момент функции из группы функций с одинаковыми именами.
В качестве дополнительных примеров реализации перегрузки операторов можно привести возможность обработки элементов списков, матриц, векторов и других сложных структур данных.
39
2.2.2. Инкапсуляция классов
Описанный выше принцип создания абстрактных типов может быть реализован в любом процедурном языке программирования (Си, Паскаль, и т.д.), поддерживающим структуры и объединения базовых типов. Однако для построения более полной модели реальных объектов реального мира требуется описать поведение объекта и возможные действия объектов в виде реакции на внешние события. Информация о внешних событиях по отношению к объекту называется «сообщением объекту», при этом объект может реагировать только на предопределенные, т.е. описанные программистом виды сообщений, присущие конкретному объекту (читай абстрактному типу данных). Иначе говоря, программист заранее создаёт функции, отвечающие за поведение объекта в том или ином случае. Процедурные языки программирования позволяют создавать искусственные структуры (абстрактные типы данных) на основании типовых базовых конструкций языка программирования и, несмотря на то, что в состав таких абстрактных типов данных могут входить также и функции (как и прочие базовые элементы и конструкции языка), назвать такую структуру самостоятельным объектом еще нельзя. Это связано с правилом доступа к элементам структур: необходимость прямой адресации каждого элемента структуры не позволяет обращаться к структуре, как к совокупности данных и функций, позволяя, однако, обращаться к каждому элементу по отдельности.
Следовательно, для реализации совокупности данных и функций в виде связок: «данные об объекте» + «функции объекта» + «описание реакции объекта на события» требуется некий новый механизм. Такой механизм имеет место в объ- ектно-ориентированных языках ( в т.ч. и в языке С++) и носит название «инкапсуляция». Иначе «инкапсуляция» – это объявление новых абстрактных типов данных, в состав которых входят атрибуты, функции и методы. Под методами следует понимать описание реакции объекта данного абстрактного типа на возможные события.
Абстрактные типы данных, имеющие одинаковые составные члены (атрибуты в виде переменных и методы в виде функций), объединяются в группы однородных типов (классы). Таким образом описать класс – это означает создать некий абстрактный тип данных, содержащий общие (для группы однородных объектов) элементы. Именно процесс описания такого класса называется «инкапсуляцией» (раскрытием содержимого класса) и, по сути, является аналогом объявления любой переменной. Инкапсуляций класса называют также интерфейс класса. Пример объявления класса (на языке С++) приведён на рисунке 8.
|
40 |
|
|
Int x; |
//объявление переменной х базового типа «int» |
class ABC{int y; ABC();int function(int);}; // объявление абстрактного типа
// «АВС»
//class – ключевое слово, означает начало процесса инкапсуляции;
//АВС – наименование класса, y – атрибут класса;
//ABC()– метод класса, отвечает за реакцию объектов данного // типа на события;
//int function(int) – функция класса, расширяет возможности класса.
Рисунок 8 – Пример объявления класса
2.2.3.Интерфейс класса и правила доступа к элементам класса
Впроцессе инкапсуляции кроме указания имени абстрактного типа (класса), типов и имён переменных, методов и функций необходимо указать также уровень доступа к этим элементам извне класса. Степень открытости класса следует рассматривать с точки зрения защищенности элементов, составляющих класс. Если элемент класса закрыт, то это означает, что его видимость ограничена пространством имён только для данного класса. Иными словами в классе декларируется уровень видимости переменных, методов и функций для будущих экземпляров класса (т.е. объектов). Понятие «декларация» использовано с целью пояснения основного принципа: объявление класса суть объявление переменой нового типа, а создание объекта данного класса суть инициализация этой переменной. При описании (инкапсуляции) класса выделение памяти не происходит, но имеет место описание того, что будет происходить в момент создания экземпляра данного класса, т.е. объекта. При создании объекта будет выделена память, размер которой декларирован в описании класса (в виде перечня базовых и
не базовых типов данных), произойдёт определение зоны видимости элементов класса, а также произойдёт выполнение декларированного в описании класса ка- кого-либо действия. Следует отметить, что за упомянутые действия несут ответственность методы класса.
Пространство имён для данного класса может быть обеспечено следующим перечнем схем доступа:
1)закрытые элементы – зона видимости ограничена интерфейсом класса;
2)открытые элементы – зона видимости ограничена программным модулем.