Пособие по ооп. С++ ООП УчебноеПособие. Объектноориентированное
Скачать 1.49 Mb.
|
Министерство образования и науки РОССИЙСКОЙ ФЕДЕРАЦИИ Федеральное государственное автономное образовательное учреждение высшего профессионального образования «ЮЖНЫЙ ФЕДЕРАЛЬНЫЙ УНИВЕРСИТЕТ» Я. М. ДЕМЯНЕНКО, М. И. ЧЕРДЫНЦЕВА ОБЪЕКТНО-ОРИЕНТИРОВАННОЕ ПРОГРАММИРОВАНИЕ В С++ Учебное пособие Ростов-на-Дону Издательство Южного федерального университета 2018 УДК 004.43 2 ББК 32.973.2 Р 88 Рецензенты: к.ф.-м.н., доцент ЮФУ М. Э. Абрамян, д.т.н., профессор РГУПС М. А. Бутакова Демяненко Я. М. Р 88 Объектно-ориентированное программирование в С++/ Я. М. Демяненко, М. И. Чердын- цева. – Ростов н/Д : Изд-во ЮФУ, 2018. – 142 с. ISBN В данном учебном пособии внимание уделяется использованию объектно- ориентированного подхода в языке С++. Изложение материала основано на рассмотре- нии конкретных примеров. Книга состоит из трёх глав. Учебное пособие адресовано студентам, обучающимся по бакалаврской программе по направлению «Прикладная ма- тематика и информатика». Может быть использовано для самостоятельного изучения объектно-ориентированного подхода средствами языка С++. ISBN УДК 004.43 ББК 32.973.2 ©Демяненко Я. М., Чердынцева М. И., 2018 © Южный федеральный университет, 2018 © Оформление. Макет. Издательство Южного федерального университета, 2018 3 СОДЕРЖАНИЕ СОДЕРЖАНИЕ ......................................................................................................................... 3 ПРЕДИСЛОВИЕ ....................................................................................................................... 4 ВВЕДЕНИЕ ............................................................................................................................. 5 ГЛАВА 1. Классы и объекты .............................................................................................. 7 1.1.Основы создания классов ................................................................................................... 7 1.2.Конструкторы и деструкторы .......................................................................................... 12 1.3.Дружественные функции ................................................................................................. 17 1.4.Перегрузка операций ........................................................................................................ 27 1.5.Перегрузка префиксной и постфиксной операций инкремента ................................... 36 1.6.Реализация преобразования типов .................................................................................. 41 1.7.Обработка исключений .................................................................................................... 49 1.8.Статические члены класса................................................................................................ 55 1.9.Члены класса создаваемые автоматически ..................................................................... 57 1.10.Семантика перемещения: move-конструктор и move-operator= ................................. 60 ГЛАВА 2. Отношения между классами .......................................................................... 66 2.1.Наследование классов ....................................................................................................... 66 2.2.Открытое наследование .................................................................................................... 67 2.3.Отношение включения ..................................................................................................... 82 2.4.Позднее связывание и виртуальные функции ................................................................ 85 2.5.Абстрактные классы ......................................................................................................... 91 2.6.Цена виртуальности и система RTTI .............................................................................. 96 2.7.Отношение подобия .......................................................................................................... 98 2.8.Коллекции и итераторы .................................................................................................. 103 2.9.Классы для рекурсивных типов данных ....................................................................... 113 ГЛАВА 3. Обобщенный подход ..................................................................................... 119 3.1.Шаблоны классов ............................................................................................................ 119 3.2.Коллекции ........................................................................................................................ 127 ЛИТЕРАТУРА ....................................................................................................................... 140 4 ПРЕДИСЛОВИЕ Эта книга продолжает серию учебных пособий по курсу «Языки про- граммирования». Учебное пособие адресовано студентам, обучающимся по бакалаврской программе по направлению «Прикладная математика и ин- форматика». Может быть использовано для самостоятельного изучения объектно-ориентированного подхода средствами языка С++. В данном издании мы стремились достичь двух целей: во-первых, по- знакомить студентов с основами объектно-ориентированного программи- рования; во-вторых, продемонстрировать приёмы решения задач с учетом особенностей языка С++. Выражаем благодарность доценту ЮФУ М. Э. Абрамяну, высказав- шему замечания, которые помогли улучшить качество и ценность книги. Мы признательны доценту ЮФУ С. С. Михалковичу за возможность вос- пользоваться некоторыми интересными примерами, а также многим нашим коллегам, преподавателями студентам, с которыми нас объединяют общие интересы. 5 ВВЕДЕНИЕ В данной книге содержится материал, важный для понимания языка программирования. Материал не ориентирован на определенную версию компилятора, внимание уделяется именно языку С++ и использованию объектно-ориентированного подхода, а не особенностям конкретной реали- зации. В главе 1 вводятся основные понятия и определения объектно- ориентированного программирования с особенностями их реализации в языке С++. Изложение материала сопровождается разбором примеров ре- шенных задач. В главе 2 рассматриваются отношения между классами. Уделяется внимание различным видам наследования. Рассматривается механизм вир- туальности и принцип полиморфизма. Изложение материала иллюстриру- ется подробным рассмотрением примеров решенных задач. Такой подход позволит студентам научиться применять теоретические знания при реше- нии конкретных задач. Глава 3 посвящена базовым понятиям обобщённого программирова- ния и особенностям их реализации в языке С++. Изучение материала по- строено на основе разбора примеров программ. Представленный в учебном пособии материал используется в курсе «Языки программирования» бакалаврской программы по направлению «Прикладная математика и информатика». Книга может быть использована для самостоятельного изучения объектно-ориентированного подхода сред- ствами языка С++. 6 Для облегчения работы с текстом в книге приняты следующие со- глашения: Коды программ, фрагменты примеров, операторы, классы, объекты, методы обозначены специальным шрифтом (Courier), что позволя- ет легко найти их в тексте. Для указания имен файлов и каталогов используется шрифт Arial. Важные термины, встречающиеся впервые, выделены курсивом. Определения, термины, объяснения для запоминания предваряются специальным символом . Более подробные объяснения отмечены специальным символом . Знак означает, что проводятся различные сравнения. Специальный символ означает рекомендации по написанию про- грамм. Предостережения от ошибок начинаются со знака Упражнения, которые необходимо выполнить по ходу изучения, обо- значены специальным символом 7 ГЛАВА 1. КЛАССЫ И ОБЪЕКТЫ 1.1. Основы создания классов Попытаемся ответить на вопрос: в чем же состоит принципиальное отличие языка С++ от языка С? Сошлемся при этом на одного из специали- стов по языку С++ Брюса Эккеля: «Включение функций в структуры со- ставляет подлинную суть того, что язык С++ добавил в С». При внесении функций в структуры к характеристикам (как у структур в С) добавляется поведение. Так возникает концепция класса. В результате чего его поля (характеристики) и функции (поведение) рассматриваются как единое це- лое. Такое объединение получило название инкапсуляция (encapsulation). При этом существует возможность регламентировать доступ к членам класса (полям и функциям). Существует три спецификатора доступа: private (закрытый) – доступен только для членов класса; protected (защищенный) – доступен для членов класса и их наследников; public (открытый) – доступен для всех. Эти спецификаторы определяют уровень доступа для всех объявле- ний, следующих за ними. После любого спецификатора должно стоять двоеточие. Если спецификатор при объявлении опущен, то используется спецификатор по умолчанию. Для объявления класса можно использовать либо ключевое слово struct, либо ключевое слово class. Рекомендуется использовать слово class. Различие проявляется в спецификаторах доступа по умолчанию (табл. 1). 8 Таблица 1 Различие в доступе по умолчанию Описание class <имя класса> { <поля и функции> }; struct <имя класса> { <поля и функции> }; Спецификатор досту- па по умолчанию private public Следующие два объявления эквивалентны: struct A{ int f(); void g(); private: int i, j, k; }; int A::f() { return i+j+k; } void A::g() { i=j=k=0; } class B{ int i, j, k; public: int f(); void g(); }; int B::f() { return i+j+k; } void B::g() { i=j=k=0; } int main(){ A a; B b; a.f(); a.g(); b.f(); b.g(); } Рекомендуется всегда явно задавать спецификатор доступа к членам класса. Приняты два варианта стиля объявлений класса: сначала располагаются поля, потом – функции; сначала располагаются функции, потом – поля. Рекомендуется в одной программе придерживаться одного из вариантов стилей объявлений. 9 Класс определяет новый тип данных. Переменная данного типа назы- вается объектом. Объект также называют экземпляром класса. Пример 1. Реализовать класс для описания геометрической фигуры «круг». Определить конструкторы, функции-члены класса для вычисления площади, функции доступа к радиусу. Реализовать сравнение объектов на равенство. //circle.h #ifndef CIRCLE_H #define CIRCLE_H class circle { private: double x,y,r; public: circle(); circle(double x1, double y1, double r1); double s(); void set_r(double r1); double get_r(); bool equal(circle a); bool operator ==(circle a); }; #endif //circle.cpp #include <сmath> #include "circle.h" circle::circle(){ x=10; y=10; r=10; } circle::circle(double x1, double y1, double r1){ x=x1; y=y1; r=r1; } double circle::s(){ return 3.14*r*r; } void circle::set_r(double r1){ if (r1<0) r=0; else 10 r=r1; } double circle::get_r(){ return r; } bool circle::equal(circle a){ const double eps=0.0001; if (abs(x-a.x) } //главный файл – main.cpp #include "circle.h" #include Описание класса размещено в заголовочном файле. В описание клас- са включены поля и заголовки функций. В C++ рекомендуется реализацию функций располагать в большинстве случаев вне класса, но допустимо и внутри описания класса. Описание класса рекомендуется располагать в заголовочном файле. Поля класса x, y, r объявлены закрытыми. Все функции этого класса – открытые. Среди функций присутствуют: два конструктора circle(); circle(double x1, double y1, double r1); 11 функция вычисления площади double s(); пара функций для установки доступа к закрытому полю класса (их на- зывают сеттеры и геттеры соответственно) void set_r(double r1); double get_r(); две функции для сравнения двух объектов на равенство – один реализо- ван как функция, другой как перегруженная операция bool equal(circle a); bool operator ==(circle a); В C++ для пользовательских типов данных возможна собственная реализация стандартных операций, которая называется перегрузкой опера- ций. Перегруженная операция – это функция, имя которой начинается со слова operator, за которым следует символ операции. Использовать перегруженную операцию можно двумя способами: традиционное использование операции cout<<(a==b)< В основном поля рекомендуется делать закрытыми, интерфейсные функции – открытыми, а служебные функции – закрытыми. Реализация функций расположена в отдельном cpp-файле. При их реализации вне описания классов имена функций должны быть уточнен- ными именем класса. Для этого используется операция разрешения области видимости :: : double circle::s(){ return 3.14*r*r; } 12 Текст файла main.cpp начинается с создания объектов a и b. Для создания объекта a вызывается конструктор по умолчанию, для объекта b – конструктор с параметрами: circle a,b(0,0,1); Все функции класса circle являются функциями объектов (экземп- ляров класса). Они вызываются через точку a.s(): cout< Конструкторы и деструкторы Чтобы обеспечить должную инициализацию каждого объекта, разра- ботчик класса должен включить в него специальную функцию, которая на- зывается конструктором. Имя конструктора должно совпадать с именем класса. Конструктор предназначен для инициализации полей объекта на- чальными значениями, он вызывается в момент создания объекта. В примере 1 реализованы два конструктора – без параметров и с па- раметрами: circle(); //без параметров – конструктор по умолчанию circle(double x1, double y1, double r1); //конструктор с параметрами Конструктор без параметров называется конструктором по умолча- нию. Если в дальнейшем предполагается размещать объекты класса в массиве или в любом другом контейнере, необходимо объявлять конструктор по умолчанию. В случае если не описан явно ни один конструктор, компилятор ав- томатически (неявно) сгенерирует конструктор по умолчанию. Поведение 13 неявно созданного конструктора будет таким же, как если бы он был объ- явлен явно без списка параметров и с пустым телом. Когда программа достигает точки выполнения, в которой определя- ется объект circle a,b(0,0,1); происходит автоматический вызов конструктора. Синтаксис деструктора в целом схож с синтаксисом конструктора: имя функции тоже определяется именем класса. Но чтобы деструктор от- личался от конструктора, его имя начинается с префикса (тильда). Кроме того, деструктор всегда один и у него нет аргументов. В случае отсутствия явного задания деструктора, компилятор неявно создаёт деструктор с пус- тым телом. Деструктор автоматически вызывается компилятором при выходе объекта из области видимости или при уничтожении объекта, размещённо- го в динамической памяти. При этом очищается память, занимаемая объек- том. Если перед уничтожением объекта необходимо освободить исполь- зуемые ресурсы (например, динамическую память для размещения полей объекта, открытые файлы и т.д.), то необходимо явно определить деструк- тор, выполняющий эти действия. Конструкторы и деструкторы обладают одной уникальной особенно- стью: они не имеют возвращаемого значения. В этом они принципиально отличаются от функций, возвращающих пустое значение (значение типа void). Чтобы лучше понять механизмы вызовов конструкторов и деструкто- ров, можно включать в их код операторы вывода. 14 Добавьте в конструкторы класса circle операторы вывода информации о том, какой конструктор сработал. Добавьте деструктор, выводящий информацию о том, что он сработал. Попробуйте объяснить, почему количество сообщений от декструктора превышает количество сообщений от конструкторов. Пример 2. Реализовать два класса для описания геометрических фи- гур «круг» и «прямоугольник». Описать функцию, позволяющую совмес- тить центры круга и прямоугольника (переместить круг), если круг можно поместить в прямоугольник. //shape.h #ifndef SHAPE_H #define SHAPE_H class rectangle; class circle { private: double x, y, r; public: circle(); circle(double x1, double y1, double r1); void print(); friend bool tofit(circle &a, rectangle b); }; class rectangle { private: double x1, y1, x2, y2; public: rectangle(); rectangle(double x1, double y1, double x2, double y2); double width(); double height(); void print(); friend bool tofit(circle &a, rectangle b); }; #endif 15 //shape.cpp #include #include #include "shape.h" using namespace std; circle::circle(){ x = 10; y = 10; r = 10; } circle::circle(double x1, double y1, double r1){ x = x1; y = y1; r = r1; } void circle::print(){ cout << x << ' ' << y << ' ' << r << endl; } rectangle::rectangle(){ x1 = 0; y1 = 0; x2 = 10; y2 = 10; } rectangle::rectangle(double x1,double y1,double x2,double y2 ){ this->x1 = x1; this->y1 = y1; this->x2 = x2; this->y2 = y2; } double rectangle::width(){ return abs(x2 - x1); } double rectangle::height(){ return abs(y2 - y1); } void rectangle::print(){ cout << x1 << ' ' << y1 << ' ' << x2 << ' ' << y2 << endl; } bool tofit(circle & a, rectangle b){ if (b.width() >= 2 * a.r && b.height() >= 2 * a.r){ 16 a.x = (b.x1 + b.x2) / 2; a.y = (b.y1 + b.y2) / 2; return true; } return false; } //главный файл – main.cpp #include "shape.h" #include } else cout << "круг не помещается в прямоугольник" << endl; return 0; } В заголовочном файле shape.h размещены описания двух классов circle и rectangle, а их определения в файле shape.cpp. В конструкторе с параметрами для класса rectangle возникает си- туация, когда имена параметров совпадают с именами переменных-членов класса. Для разрешения коллизии имён используется ключевое слово this. rectangle::rectangle(double x1,double y1,double x2,double y2 ){ this->x1 = x1; this->y1 = y1; this->x2 = x2; this->y2 = y2; } 17 Ключевым словом this обозначается указатель на объект в функци- ях-членах класса. Если коллизии имён не возникают, то при обращении к членам класса его обычно опускают. Указатель на объект передаётся как неявный параметр во все функции, которые могут вызываться только для экземпляров классов (в том числе, конструкторы и деструкторы). |