Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Voprosy_IGME.doc
Скачиваний:
9
Добавлен:
01.05.2025
Размер:
3.64 Mб
Скачать
  1. Определение термина «инкапсуляция» в языках объектно-ориентированной парадигмы программирования. Модульность. Примеры инкапсуляции.

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

Пример:

class mon { private: int a, b; //скрытые поля public:

mon(int x=100, int y=20){a=x; b=y;} int get_a () {return a;} int get_b() {return b;} };

Описание класса mon разделено на закрытую и от­крытую части, помеченные как private и public. От­крытая часть (public) образует открытый интерфейс объектов класса. Имена закры­той части (private) могут использоваться только функциями-членами и друзьями класса.

Модульность

Модулем называют набор связанных процедур вместе с данными, которые они обрабатывают.

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

В языке C++ модулями являются файлы, которые компилируются отдельно один от другого и затем объединяются в один исполняемый файл при помощи редактора связей.

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

  •  Интерфейс стека будет помещен в заголовочный файл - stack.h.

Заголовочный файл обычно включается и в файл с пользовательским кодом, и в файл с реализацией модуля.

  •  Код пользователя находиться в файле - user.c:

#include "stack.h" // включить интерфейс

main (void) { . . .}

  •  Файл, содержащий реализацию модуля Stack называться - stack.c:

#include "stack.h" // включить интерфейс

int stack [100]; //реализация

int top;

void push (int el){. . .}

int pop ( ){. . .}

Тексты user.c и stack.c совместно используют информацию об интерфейсе, содержащуюся в stack.h. Во всем другом эти два файла независимы и могут быть раздельно откомпилированы.

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

  1. Определение термина «наследование». Определение базового класса. Соотношение подкласса и суперкласса. Пример записи наследования в программе. Типы наследования: внутреннее, защищенное и публичное наследование, назначение и примеры записи в программе.

Наследование – процесс, в результате которого один тип наследует свойства другого типа. Т.е. один класс обладает всеми свойствами другого класса, т.к. он их унаследовал. У объекта производного класса есть все атрибуты и методы базового класса. Новый класс может добавить собственные атрибуты и методы.

Наследование позволяет создать иерархию классов.

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

Суперкласс – базовый класс.

Типы наследования:

Ключевое слово public перед именем базового класса определяет, что внешний интерфейс базового класса становится внешним интерфейсом порожденного класса. Это наиболее употребляемый тип наследования.

Если перед именем базового класса ставится ключевое слово private, то наследование называется внутренним.

class B : private A

{. . .};

В случае внутреннего наследования внешняя и защищенная части базового класса становятся внутренней частью производного класса. Внутренняя часть базового класса остается для производного класса недоступной.

Если перед именем базового класса поставить ключевое слово protected, то будет использоваться защищенное наследование. При нем внешняя и защищенная части базового класса становятся защищенной частью производного класса. Внутренняя часть базового класса остается недоступной для производного класса.

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

Множественное наследование – мощное средство языка. Приведем некоторые примеры использования множественного наследования.

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

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

class Annotation { public: String GetText(void); private: String annotation;}; class Shape

{public: virtual void Draw(void); }; class AnnotatedSquare : public Shape, public Annotation

{ public:virtual void Draw();};

У объекта класса AnnotatedSquare имеется метод GetText, унаследованный от класса Annotation, он определяет виртуальный метод   Draw, унаследованный от класса Shape.

При применении множественного наследования возникает ряд проблем. Первая из них – возможный конфликт имен методов или атрибутов нескольких базовых классов.

class A { public: void fun(); int a; }; class B { public: int fun(); int a; };class C : public A, public B

{};

При записи

C* cp = new C;

cp->fun();

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

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

cp->A::fun();

Вторая проблема заключается в возможности многократного включения базового класса. В упомянутом выше примере интеграции библиотечной системы и системы кадров вполне вероятна ситуация, при которой классы для работников библиотеки и для студентов были выведены из одного и того же базового класса   Person:

class Person

{public:

String name();};

class Student : public Person{

. . .};class Librarian : public Person{. . .};

  1. Виртуальные методы. Абстрактные классы. Множественное наследование: примеры использования, проблемы использования (конфликт имен методов или атрибутов нескольких базовых классов; многократное включение базового класса). Механизм позднего и раннего связывания

Виртуальный метод - метод ссылка на который разрешается на этапе выполнения программы (перевод английского слова virtual - в данном значении «фактический», то есть ссылка разрешается по факту вы­зова).

Пример:

virtual void draw (int x, int y);

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

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

- Если в базовом классе метод определен как виртуальный, метод, определенный в производном классе с тем же именем и набором параметров, автоматически становится виртуальным, а с отличающимся набором параметров — обычным.

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

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

- Виртуальный метод не может объявляться с модификатором static, но может быть объявлен как дружественный.

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

Чисто виртуальный метод содержит признак = 0 вместо тела, например:

virtual void d (int) = 0;

Абстрактный класс в объектно-ориентированном программировании — базовый класс, который не предполагает создания экземпляров. Абстрактные классы реализуют на практике один из принципов ООП - полиморфизм. Абстрактный класс может содержать (и не содержать[1]абстрактные методы и свойства. Абстрактный метод не реализуется для класса, в котором описан, однако должен быть реализован для его неабстрактных потомков. Абстрактные классы представляют собой наиболее общие абстракции, то есть имеющие наибольшийобъём и наименьшее содержание.

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

Абстрактные методы часто являются и виртуальными, в связи с чем понятия «абстрактный» и «виртуальный» иногда путают.

Множественное наследование – мощное средство языка. Приведем некоторые примеры использования множественного наследования.

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

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

class Annotation { public: String GetText(void); private: String annotation;}; class Shape

{public: virtual void Draw(void); }; class AnnotatedSquare : public Shape, public Annotation

{ public:virtual void Draw();};

У объекта класса AnnotatedSquare имеется метод GetText, унаследованный от класса Annotation, он определяет виртуальный метод   Draw, унаследованный от класса Shape.

При применении множественного наследования возникает ряд проблем. Первая из них – возможный конфликт имен методов или атрибутов нескольких базовых классов.

class A { public: void fun(); int a; }; class B { public: int fun(); int a; };class C : public A, public B

{};

При записи

C* cp = new C;

cp->fun();

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

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

cp->A::fun();

Вторая проблема заключается в возможности многократного включения базового класса. В упомянутом выше примере интеграции библиотечной системы и системы кадров вполне вероятна ситуация, при которой классы для работников библиотеки и для студентов были выведены из одного и того же базового класса   Person:

class Person

{public:

String name();};

class Student : public Person{

. . .};class Librarian : public Person{. . .};

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

Преимущества динамического связывания: изменение одного модуля не требует перекомпиляции взаимодействующих с ним модулей; уменьшение размеров программы

Недостаток динамического связывания: замедление скорости выполнения программы.

Статическое (раннее) связывание – связывание имени со значением происходит во время компиляции.

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

  1. Определение термина «полиморфизм». Разновидности полиморфизма в парадигме объектно-ориентированного программирования. Полиморфные переменные. Перегрузка функций. Параметрическая перегрузка. Чистый полиморфизм.

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

В объектно-ориентированных языках программирования полиморфизм — естественное следствие:

  • отношения «быть экземпляром»;

  • механизма пересылки сообщений;

  • наследования;

  • принципа подстановки.

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

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

В языках с динамическим связыванием (Smalltalk, Objective-C) все переменные являются потенциально полиморфными (любая переменная может содержать значение любого типа). В этих языках от типа требуется только некий набор ожидаемых действий. Например, массив — это то, что по индексу поставляет значение. Иными словами, пользователь может определить свой собственный тип данных (скажем, разреженный массив), и если операции индексирования определены с теми же именами, то новый тип данных может использоваться в уже существующем алгоритме.

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

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

Другой стиль перегрузки, при котором процедурам (функциям, методам) в одном и том же контексте разрешается использовать совместно одно имя, а двусмысленность снимается за счет анализа числа и типов аргументов, называется параметрической перегрузкой. Она присутствует в C++ и Java, а также в некоторых директивных языках (например, Ada) и во многих языках, основанных на функциональной парадигме. (При автоматическом приведении типа — например, от символов character к целым числам integer или от целых integer к числам с плавающей точкой float — алгоритм, используемый для разрешения неоднозначности в имени перегруженной функции, становится очень сложным.

Многие авторы резервируют понятие полиморфизм (или чистый полиморфизм) для ситуаций, когда одна функция используется с разными наборами аргументов, и термин перегрузка — для случая, когда определено несколько функций с одним именем1. Эти термины не ограничены исключительно объектно-ориентированным подходом. Например, в языках Lisp и ML легко написать функции, которые обрабатывают списки с различными элементами. Такие функции являются полиморфными, поскольку тип аргумента неизвестен при определении функции. Полиморфные функции — это одна из наиболее мощных объектно-ориентированных техник программирования. Они позволяют единожды писать код на высоком уровне абстрагирования и затем применять его в конкретной ситуации. Обычно программист выполняет подгонку кода с помощью посылки дополнительных сообщений получателю, использующему метод. Эти дополнительные сообщения часто не связаны с классом на уровне абстракции полиморфного метода. Они являются виртуальными методами, которые определяются для классов более низкого уровня.

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]