550 likes | 813 Views
Основы ООП. Объектно-ориентированное программирование. Парадигма программирования, основанная на представлении предметной области в виде взаимосвязанных абстрактных объектов и их реализаций. Классы и объекты.
E N D
Объектно-ориентированное программирование • Парадигма программирования, основанная на представлении предметной области в виде взаимосвязанных абстрактных объектови их реализаций
Классы и объекты • В ООП вводится понятие Класса – пользовательского типа данных, объединяющего данные и методы их обработки • Объектом называется экземпляр класса • Собака – это класс • Собака Жучка из 3 подъезда – это объект, представитель или экземпляр класса «Собака»
Объявление классав С++ class<Имя класса> { // поля класса (данные и методы) };
Данные объекта (переменные объекта, члены-данные) • Члены-данные(data members) хранят всю необходимую информацию об объекте, формируют его состояние, характеристики и т.п. • Изменение состояния объекта или его характеристик связано с изменением данных, в нем содержащихся
Методы класса • Класс может содержать один или более методов, позволяющих осуществлять манипуляцию данными объекта • Метод объекта– программный код, выполненный в виде процедуры или функции, реагирующий на передачу объекту определенного сообщения • Вызов метода объекта может приводить к изменению его состояния (значение членов-данных), а может и не приводить • Пример 1: поиск и замена текста в документе • Пример 2: проверка правописания текста документа
Свойства • Свойство – составляющая часть объекта, доступ к которой осуществляется программистом как к переменной объекта • В некоторых объектно-ориентированных языках программирования (например, в C++ и Java) свойства, как элемент языка, отсутствуют • В этом случае в класс добавляют методы, посредством которых осуществляется доступ к необходимым переменным класса
Пример: Треугольник • Свойства • Координаты вершины A • Координаты вершины B • Координаты вершины C • Площадь • Периметр • Координаты центра вписанной окружности • Методы • Переместить в заданном направлении • Отмасштабировать • Повернуть вокруг заданной точки
classPoint { public: doublex, y; }; classTriangle { public: doubleGetArea(); doubleGetPerimeter(); PointGetCenter(); voidMove(doubledx, doubledy); voidScale(doublesx, doublesy); voidRotate(Pointcenter, doubleangle); Point p0, p1, p2; };
Важнейшие принципы ООП • Абстракция данных • Инкапсуляция • Наследование • Полиморфизм
Абстракция данных • Объекты представляют неполную информацию о реальных сущностях предметной области • Абстракция позволяет оперировать с объектном на уровне, адекватном решаемой задаче • Высокоуровневые обращения к объекту могут обрабатываться с помощью вызова функций и методов низкого уровня
Инкапсуляция • Инкапсуляция - способность объекта скрывать внутреннее устройство своих свойств и методов • Согласно данному принципу, класс должен рассматриваться как черный ящик • Внешний пользователь не знает детали реализации объекта и работает с ним только путем предоставленного объектом интерфейса • Следование данному принципу может уменьшить число связей между классами и упростить их независимую реализацию, модификацию и тестирование
Пример. Стек целых чисел classIntStack { public: voidPush(int value); int Pop(); bool IsEmpty()const; private: // здесь располагаются данные // необходимые для реализации стека целых чисел };
Наследование • Наследование позволяет описать новый класс на основе уже существующего родительского(базового) класса • Класс-потомок может добавить свои собственные свойства и методы, пользоваться методами и свойствами базового класса • Наследование позволяет строить иерархии классов
Пример classPlane { public: voidTakeOff(); voidFly(); voidLand(); private: doublem_fuel; }; classMilitaryPlane : publicPlane { public: voidAttack(); private: int m_ammo; };
Полиморфизм • Полиморфизмом называют явление, при котором классы-потомки могут изменять реализацию метода класса-предка, сохраняя его интерфейс • Полиморфизм позволяет обрабатывать объекты классов-потомков как однотипные объекты, не смотря на то, что реализация методов у них может различаться
classShape { public: virtualdoubleGetArea()=0; }; classRectangle : publicShape { public: virtualdoubleGetArea() { returnwidth * height; } private: doublewidth, height; }; classCircle : publicShape { public: virtualdoubleGetArea() { return 3.1415927 * radius * radius; } private: doubleradius; };
Объявление класса в C++ • Для объявления класса в C++ служит ключевое слово class • Синтаксис • class идентификатор{ // объявление данных и методов}; • Реализация методов класса может быть вынесена за пределы объявления класса
Пример class Date { int year, month, day; void next(); void print(); }; // Реализация методов класса void Date::print() { printf(“%d/%d/%d”, day, month, year); } void Date::next() { // ... }
Размещение классов в различных файлах • Общепринятой практикой является размещение объявления классов в заголовочных файлах .h, а их реализации – в файлах .cpp • Повышение модульности проекта • Каждый класс может быть подключен для дальнейшего использования при помощи директивы #include“имя заголовочного файла” • При внесении изменений в реализацию метода класса перекомпиляции подвергнутся только измененные файлы
Пример date.h class Date { public: void Next(); void Print(); private: int m_day; int m_month; int m_year; }; date.cpp #include “date.h” void Date::Next() { // ... } void Date::Print() { // ... } main.cpp #include “date.h” int main() { Date date1; return 0; }
Ограничение доступа к данным и методам класса • Доступ к данным и методам класса извне может быть ограничен • Рекомендуется запрещать доступ к данным класса в обход его методов • Для разделения прав доступак полям класса используются ключевые слова • public: • private: • protected:
Публичные (public) поля класса • Public-методы и данные класса определяют его интерфейс • доступ к ним возможен из любой части кода • необходимо помещать в public-раздел класса только необходимый набор методов, выполняющих высокоуровневые операции над объектом класса
Закрытые (частные) поля класса • Private-данные и методы класса определяют его реализацию • Доступ к ним разрешен только из методов данного класса • Рекомендуется все данные класса делать закрытыми,их обработку осуществлять внутри методов • Закрытые методы класса обычно используются публичными методами, решая внутренние задачи класса
Защищенныеполя класса • Protected-данные и методы определяют интерфейс для производных классов • Доступ к ним разрешен изнутри методов данного класса и всех его потомков • В защищенной зоне размещают методы, которые не должны быть видны снаружи класса, но реализация которых может быть переопределена или использована производными классами
Пример class Date { public: void Next(); void Print(); private: int year, month, day; }; // Реализация методов класса void Date::Print() { printf(“%d/%d/%d”, day, month, year); } void Date::Next() { // ... }
Ссылка на себя • Внутри методов класса для обращения к данным класса можно использовать их имена • В метод класса неявно передается указатель на объект, для которого он вызывается • Доступен данный указатель по ключевому слову this
Пример class ListItem { public: void Append(ListItem *pItem) { pItem->m_pNext = this; m_pPrevious = pItem; m_pNext = NULL; } private: ListItem *m_pNext; ListItem *m_pPrevious; int m_data; };
Константные методы • В языке C++ методы объекта, не изменяющие его состояния (его данных) могут быть объявлены константными • Например, методы, возвращающие значения определенных полей данных • Изменить данные класса из константного метода нельзя
Когда возникает необходимость в константных методах • Если объект был объявлен как константа, либо доступен по константной ссылке или указателю на const, то вызвать у него можно только константные методы • Это заставляетобъявлять методы константными везде, где это только возможно
Пример class IntArray { public: … int GetSize()const { return m_numberOfItems; } void ClearElements() { delete [] m_pData; m_pData = NULL; m_numberOfItems = 0; } private: int *m_pData; int m_numberOfItems; }; void f(IntArray const& array) { int i = array.GetSize(); // можно array.ClearElements(); // нельзя – неконстантные методы недоступны }
Изменчивые (mutable) данные класса • Данные класса, которые все-таки нужно изменять из константных методов класса в С++ объявляются с ключевым словом mutable • Пользоваться этой возможностью следует аккуратно, четко осознавая, что даже в этом случае константные методы не должны изменять состояние объекта • Под состоянием объекта здесь понимается информация о нем, доступная посредством публичных методов
Пример class VeryComplexShape { public: VeryComplexShape() { m_areaInitialized = false; } double GetArea()const { if (!m_areaInitialized) { // вычисляем площадь фигуры (задача требует длительных вычислений) m_areaInitialized = true; … } return m_area; } void ModifyShape(...) { m_areaInitialized = false; // ... } private: mutable bool m_areaInitialized; mutable double m_area; };
Инициализация экземпляра класса • Для инициализации состояния объекта в момент его создания существует специальная функция – конструктор • Конструктор имеет то же имя, что и имя класса • Тип возвращаемого значения для конструктора не указывается (даже void) • Конструктор вызывается в момент создания экземпляра класса (объявление переменной класса или вызов оператора new) • Класс может иметь несколько конструкторов, предоставляющих различные способы инициализации объекта
Пример class Date { public: Date(int day, int month) { m_day = day; m_month = month; m_year = GetCurrentYear(); } Date(int day, int month, int year) { m_day = day; m_month = month; m_year = year; } private: int m_day, m_month, m_year; };
Конструкторпо умолчанию • Конструктор, не имеющий параметров, называется конструктором по умолчанию • Поля данных в таком конструкторе инициализируются значениями по умолчанию • Создавать такой конструктор или не создавать – зависит от конкретной задачи
Инициализация данных экземпляра класса • В качестве данных класса могут выступать другие классы • Их инициализация осуществляется ДО выполнения тела конструктора • Для их инициализации вызываются конструкторы по умолчанию • Если таковых не имеется, программист должен использовать списки инициализации
Списки инициализации • Применяются для инициализации полей класса в конструкторе ДО выполнения его тела • Использование списков инициализации – единственное решение в случае, когда класс содержит внутри себя • поля, являющиеся классами без конструкторов по умолчанию • константы • ссылки
Пример class Foo { public: Foo(int i, int j = 0) :m_i(i) ,m_j(j) { } private: int m_i, m_j; }; class Bar { public: Bar() :m_foo(3, 5) { } Bar(int i, int j) :m_foo(i, j) { } private: Foom_foo; };
Деинициализация экземпляра класса • В ходе своей работы объект может использовать определенные системные ресурсы • Динамическая память, открытые файлы, сетевые соединения и т.п. • Для освобождения этих ресурсов служит особый метод класса – деструктор • Имя деструктора совпадает с именем класса, только перед ним указывается символ ~(тильда) • Данная функция вызывается автоматически при уничтожении экземпляра класса: • Выход за пределы блока, в котором объявлен экземпляр класса • Вызов оператора delete или delete []
Пример class MyFile { public: MyFile():m_pFile(NULL) {} ~MyFile() { Close(); } bool Open(const char *fileName) { Close(); m_pFile = fopen(fileName, “r”); return m_pFile != NULL; } void Close() { if (m_pFile){fclose(m_pFile); m_pFile = NULL;} } private: FILE *m_pFile; };
Конструктор копирования (копирующий конструктор) • В языке C++ существует специальный тип конструкторов, использующийся для создания копии объекта • Явное создание копииобъектапрограммистом • Неявное создание копии объекта • Возврат объекта из функции • Передача объекта в функцию по значению • Во время работы механизма исключений • Синтаксис • Type(Type const& t);
Автоматически сгенерированный конструктор копирования • Если программист не определит конструктор копирования явно, компилятор сгенерирует его во время компиляции • Автоматически сгенерированный конструктор копирования осуществляет копирование всех полей класса, вызывая для них их конструкторы копирования
#include "stdio.h" classFoo { public: Foo():m_moo(0) { } Foo(Fooconst&foo) :m_moo(foo.m_moo) { printf("Creatingcopyoffoo\n"); } private: int m_moo; }; class Bar { public: void Do() { printf("Do\n"); } private: Foom_foo; }; void f(Bar b) { printf("f()\n"); b.Do(); } Bar g() { printf("g()\n"); Bar b; return b; } int main() { Bar b0; printf("Call f()\n"); f(b0); printf("Call g()\n"); Bar b1 = (g()); b1.Do(); return 0; } OUTPUT: Call f() Creating copy of foo f() Do Call g() g() Creating copy of foo Do
Создание собственного конструктора копирования • Часто возникают ситуации, когда автоматически сгенерированный конструктор копирования не подходит • Пример – класс, реализующий массив • Стандартный конструктор копирования просто скопирует значение указателя на элементы массива, в то время как необходимо выделить в динамической памяти новый массив и скопировать в него данные из оригинального массива • В этом случае программист должен разработать собственный конструктор копирования
Пример #include "stdio.h" #include "memory.h" classIntArray { public: IntArray():m_pData(NULL), m_size(0){} IntArray(IntArrayconst&arr) :m_pData(new int [arr.m_size]) ,m_size(arr.m_size) { if (m_size != 0) { memcpy(m_pData, arr.m_pData, sizeof(int) * m_size); } } private: int * m_pData; int m_size; };
Запрещение копирования объектов • Возможны ситуации, когда операция копирования объекта не имеет смысла и должна быть запрещена • Класс, инкапсулирующий сетевое соединение • Класс, инкапсулирующий работу с файлом • Объект должен существовать в единственном экземпляре внутри приложения, например, «клавиатура» • Для запрещения копирования объекта, конструктор копирования объявляется в закрытой (private) области класса • Реализацию данного конструктора можно не писать
Пример class CFile { public: // … private: CFile(Cfile const&); // … };