ООП. Пособие по ООП в С++. Создание сложных программных систем Объектно ориентированная технология
Скачать 2.1 Mb.
|
Ограничение доступа – сокрытие отдельных элементов реализации абстракции. Необходимость ограничения доступа предполагает присутствие в абстракции (классе) двух частей: Интерфейс - доступных извне элементов реализации абстракции (состояния и поведения); Реализация – совокупность недоступных извне элементов реализации абстракции (внутренняя реализация абстракции и механизмы реализации ее поведения). Инкапсуляция – объединение всех свойств объекта, определяющих его состояние и поведение в единую абстракцию и ограничение доступа к реализации. Суть инкапсуляции - доступ к данным разрешен только методам класса. Наследование – создание нового класса на основе отношения «общее - часть» Полиморфизм – переопределение наследниками методов выполняющих одну задачу, но по разному для разных классов иерархии. 5.2 Класс в объектно – ориентированных языках программирования В теории объектно - -ориентированного программирования класса определяет множество объектов, имеющих одинаковый набор свойств и поведение. В объектно – ориентированном программировании класс - инструмент построения новых типов – пользовательских, т.е. определяет пользовательский тип данных Этим типом так же удобно пользоваться, как и встроенными и использование пользовательского типа не должно отличаться от использования встроенных типов. Тип представляет в программе некоторое понятие, так тип int представляет целое знаковое число, а unsigned int – беззнаковое целое, а также набор операций, способы представления констант этого типа. Новый тип создается для того, чтобы представить в программе некоторое понятие, которое не может быть представлено встроенным типом. Очень удобно сопровождать программу, в которой типы хорошо представляют используемые в задаче понятия, задача и программа оперируют одинаковыми понятиями. Хорошо подобранное множество пользовательских типов делает программу более понятной и позволяет транслятору обнаруживать недопустимое использование объектов, которое в противном случае останется не выявленным до отладки программы. Главное назначение в определении нового типа в программе - это отделить несущественные детали реализации (например, расположение данных в объекте нового типа ) от тех его характеристик, которые существенны для правильного его использования (например, полный список функций, имеющих доступ к данным). Такое разделение обеспечивается тем, что вся работа со структурой данных и внутренние, служебные операции над нею доступны только через специальный интерфейс. Новый тип создается для того, чтобы представить в программе некоторое понятие, которое не может быть представлено встроенным типом. Очень удобно сопровождать программу, в которой типы хорошо представляют используемые в задаче понятия: задача и программа оперируют одинаковыми понятиями. Хорошо подобранное множество пользовательских типов делает программу более понятной и позволяет транслятору обнаруживать недопустимое использование объектов, которое в противном случае останется не выявленным до отладки программы. Класс это абстрактный тип данных, определяемый пользователем. Он представляет модель реального объекта через данные (состояние) и функции по обработке этих данных (поведение). 5.3 Структура класса Класс включает две части: интерфейс – внешнее устройство класса – операции доступные всем экземплярам класса – обязательства класса перед своими клиентами. Обеспечивает воздействие одного объекта на другой с целю передать сообщение. Главное в интерфейсе – объявление операций, поддерживаемых всеми экземплярами класса. Три части интерфейса: Закрытая (private) – члены класса доступны только методам этого класса, друзьям. Открытая(public) - члены класса доступны клиентам. Защищенная (protected) – члены класса доступны наследникам, друзьям. реализация – внутреннее устройство класса – реализация (определение функций) операций объявленных в интерфейсной части. Состояние объекта задается в его классе через определения констант или переменных, помещенных в закрытую или защищенные части класса. 5.4 Формат определения класса С++ Реализация объектно – ориентированного подхода вС++ может быть выполнена на основе типа struct и на основе класса (class). Тип struct не поддерживает принцип наследования, а следовательно и принцип полиморфизма. Класс это абстрактный тип данных, определяемый пользователем. Он представляет модель реального объекта через данные (состояние) и функции по обработке этих данных (поведение). Членами класса являются: • Члены данных (поля данных) - это свойства или характеристики элемента класса - данные класса, реализующие состояние; • Члены функции (методы) – это функции класса, представляют поведение объектов класса. Состояние объекта определяют поля данных, например, компьютер включен или выключен. Формат объявления класса в С++. сlass имя класса { [private:] закрытые члены класса ] public: открытые члены класса (интерфейс класса) [protected:] защищенные члены класса ] }; 5.5 Реализация инкапсуляции через спецификаторы доступа Для обеспечения скрытия данных и поддержки инкапсуляции в классах С++ используются уровни доступа к членам класса: private - список полей и методов объявленных после спецификатора private будет доступен только методам этого класса и друзьям. public - члены класса доступные другим функциям и объектам программы. Методы этой части класса представляют интерфейс класса. protected – члены класса доступные в классе, наследникам, друзьям. Примечание. При объявлении класса, не обязательно объявлять три спецификатора доступа, и не обязательно их объявлять в таком порядке. Но лучше сразу определиться с порядком объявления спецификаторов доступа, и стараться его придерживаться. Ограничение доступа к членам класса предоставляет следующие преимущества: • Доступ к данным ограничен явно указанным списком функций. Следовательно, любая ошибка в данных могла быть внесена только методами класса и она может быть локализована ее на стадии отладки, т.е. до выполнения программы • Программисту, использующему класс достаточно знать только объявления методов и их функционал. Пример определения типа на основе класса и struct struct Pupil { char Fam[20], Name[10]; unsigned short Nclass; unsigned char P; char Adress[40]; void input_pupil(); void out_pupil(); }; Реализация метода вне структуры void Pupil::input_pupil() { cout<<"Фамилия:"; cin>>Fam; cout<<"Имя:"; cin>>Name; cout<<"Номер класса:"; cin>>Nclass; cout<<"Пол:"; cin>>P; cout<<"Адрес:"; cin>>Adress; } Тип struct не имеет уровней защиты для своих членов. Они все имеют уровень защиты public – т.е. к членам структуры могут обращаться внешние функции. Определить тип Ученик на основе класса, включив две части в класс: данные и интерфейс. class CPupil { char Fam[20], Name[10]; unsigned short Nclass; unsigned char P; char Adress[40]; public: //интерфейс класса void input_pupil(); void out_pupil(); }; Реализация функций – членов вне класса выглядит так же, как и для структуры void СPupil::input_pupil() { cout<<"Фамилия:"; cin>>Fam; cout<<"Имя:"; cin>>Name; cout<<"Номер класса:"; cin>>Nclass; cout<<"Пол:"; cin>>P; cout<<"Адрес:"; cin>>Adress; } Внешним функция мдоступ к данным закрыт. void input_pupil(CPupil U) { cout<<"Фамилия:"; cin>>U.Fam; //ошибка } 5.6 Глобальные и локальные классы Глобальные классы – это классы, объявленные вне главного блока. Локальные классы - это классы, объявленные внутри любого блока функции или другом классе. Для них характерно: • Локальный класс не может иметь статических элементов. • Методы класса могут быть реализованы только внутри класса. • Если один класс вложен в другой, то они не имеют каких-то особенных способов доступа к членам класса, для них также действуют общие правила доступа. 5.7 Члены данных в классе 5.7.1 Переменные экземпляра Могут быть представлены: • переменными любого типа, кроме типа этого же класса; • указателями, ссылки, в том числе и на этот класс; • переменными со спецификатором const, которые инициализируются один раз конструктором класса, их значения нельзя изменять; Примечание. Инициализация полей при описании не допускается. Имя класса принято начинать с большой буквы, последующие слова в имени также должны начинаться с большой буквы, например, ClassRectangl. Пример определения полей в гипотетическом классе X class X { static string s1; static const double pi=3.14; const int N=100; private: int x,y; double c,d; char *s2; int a[30]; int *b; X *myClass; //ссылка на объект класса X aa; // не верно: не может быть объектом этого класса }; 5.7.2 Переменные класса – статические переменные Это статические переменные в классе. Переменными со спецификатором static – это статические поля – общие для всех экземпляров класса, являются переменными класса, а не экземпляра. У каждого создаваемого объекта класса свои члены - данных – это переменные данного объекта, они могут быть изменены. Но в некоторых приложениях необходимо, чтобы в классе была определена переменная, общая для всех его объектов. Чтобы сделать переменную общую для всех объектов класса, ее надо объявить статической, она будет храниться в долговременной памяти и принадлежать классу, а не объекту. Например, как автоматизировать процесс формирования уникального для данного банка номера банковского счета, чтобы при создании нового объекта – нового счета – номер был определен программно. Т.е. у всех объектов Счет должна быть общая переменная, от значения которой будет создаваться номер счета, например увеличением этой переменной на 1. Определение класса лучше сохранить в заголовочном файле, а его реализацию в файле cpp. Для этого примера надо инициализировать статическую переменную и это лучше сделать в файле реализации. Файл "Bank.h" class Bank { //переменная для выделения каждому объекту своего номера счета //номер счета данного объекта int nAccountNumber; //текущий баланс double dBalance; protected: static int nNextAccountNumber; public: Bank() { //следующий номер счета nAccountNumber=++nNextAccountNumber; Cleare(); //вызывает другой метод } void Cleare() { dBalance=0.0; } int get_nAccountNumber() { return nAccountNumber; } }; Файл Bank.cpp #include "StdAfx.h" #include "Bank1.h" //инициализация статической переменной int Bank1::nNextAccountNumber=0; Основная программа #include "stdafx.h" #include #include "Bank.h" using namespace std; int main( int argc, _TCHAR* argv[]) { Bank1 B1;//вызов конструктора по умолчанию B1.set_nNextAccountNumber(); std::cout<< "nNextAccountNumber B1=" < "nNextAccountNumber B1=" < Результат Правила использования статических переменных 1. Инициализировать в классе нельзя, это надо сделать в программе, лучше в файле реализации. 2. Обращаться к полю через имя класса. 3. Статическое поле будет создано в единственном числе, т.е. не будет создаваться для каждого объекта. 4. Методы класса могут к ней обращаться. 5.8 Функции - члены в классе (методы) Это функции, реализующие: • выполнение операций над данными класса; • обеспечивающие обмен сообщениями между объектами классов приложения. Им разрешен доступ к членам – данным класса как к глобальным переменным, т.е. методы видят поля данных класса. 5.8.1 Виды функций – членов класса Конструктор – метод, который создает экземпляр (объект класса) и инициализирует поля. Деструктор – метод, обеспечивающий удаление объекта. После удаления доступ к объекту закрыт. Методы – операции над объектами класса (вычисления на данных и изменение значений полей класса (get и set)). Дружественные функции – это функции, которым разрешен доступ к членам класса как к собственным переменным, но не являющиеся методами класса. Константные методы – имеют спецификатор const после списка параметров. Такой метод не может изменить состояние объекта, т.е. изменять значения полей. Статические методы – реализуют операции над статическими полями данных класса. Являются методами класса, а не экземпляра, в них не доступен указатель this. 5.8.2 Место реализации функций - членов класса Функции - члены могут быть определены (реализованы) внутри класса или объявлены в классе. Реализация объявленных методов выполняется вне класса. При этом к имени метода приписывается префикс принадлежности имени (оператор видимости) Имя класса::Имя метода. Пример класса Рациональное число(РЧ) РЧ – это отношение числителя и знаменателя. В классе числитель и знаменатель представлены целыми числами, при этом знаменатель не равен нулю. Методы: сложение, вычитание, изменение числителя и знаменателя, наибольший общий делитель для сокращения дроби. Требуется контроль знаменателя. class Droby { private: int a,b; int Nod(); //реализация вне класса public: Droby(int a1, int b1); //объявление void Add(Droby &d); //реализация вне класса void set_a(int a1){a=a1;} //реализация в классе void set_b(int b1){b=b1;} //реализация в классе int get_a(){return a;} //реализация в классе int get_b(){return b;} //реализация в классе Droby& Sub(Droby &d); //реализация вне класса }; //реализация объявленных методов Droby::Droby(int a1, int b1) { a=a1; b=b1;int N=Nod();a= a/N; b= b/N; } int Droby::Nod() { if (a && !b || b && !a) { return a+b; } int a1=a, b1=b; while(a1!=b1) { if (a1>b1) a1=a1-b1; else b1=b1-a1; } return a1; } void Droby::Add(Droby &d) { int a1=a*d.b+b*d.a; b=b*d.b; a=a1; int N=Nod(); a=a/N; b=b/N; } Droby& Droby::Sub(Droby &d) { Droby R; a=a*d.b-b*d.a; b=b*d.b; int N=Nod(); a /= N; b/=N; return *this; } 5.8.3 Функции члены класса inline (подстановки) В ООП при разработке классов создается много маленьких функций, это порождает много вызовов, что затратно по времени. В С++ преодолеть эту трудность помогают функции - подстановки (inline). Такие функции можно определить в классе, а можно объявить в классе как обычную функцию – член, а реализовать вне класса как функцию inline. Тела функций встраиваются в код на месте вызова этой функции при выполнении препроцессорной обработки. Пример определения и объявления inline функции – члена class Droby { private: int a,b; int Nod(); public: Droby( int a1, int b1); Droby(){a=0;b=1;} void Add(Droby &d); inline void set_a( int a1){a=a1;} //подстановка в классе void set_b( int b1); //подстановка, реализация вне класса int get_a(){ return a;} int get_b(){ return b;} Droby& Sub(Droby &d); }; //реализация объявленных методов inline void Droby::set_b( int b1) { b=b1; } 5.8.4 Методы (функции – члены) экземпляра Это нестатические функции - члены. 5.8.4.1 Указатель this Этот указатель определен в каждом методе экземпляра класса неявно. Например, можно представить, что в каждом методе класса дроби присутствует оператор: Droby *const this; Он неявно инициализируется объектом, для которого был вызван метод. Используется для явного доступа к членам класса в методе. Этот указатель нельзя изменять, поскольку он постоянный (*const) и явно описать его тоже нельзя, т.к . this - ключевое слово. this используется в функциях – членах, непосредственно работающих с указателями. Пример использования указателя this для возврата измененного в методе объекта Метод вычитания возвращает объект класса, из которого выполнено вычитание Droby& Droby::Sub(Droby &d) { a=this->a*d.b-b*d.a; b=b*d.b; int N=Nod(); this->a/=N; //можно просто а this->b/=N; return *this; //возвращает измененный объект } Пример использования указателя this для ссылки на переменные объекта когда имена параметров совпадают с именами полей класса Droby::Droby(int a, int b) { this->a=a; this->b=b;int N=Nod();this->a= this->a/N; this->b= this->b/N; } Пример - ошибка. Функция возвращает локальный объект Droby& Droby::Sub(Droby &d) { Droby R; R.a= a*d.b-b*d.a; R.b=b*d.b; int N=R.Nod(); R.a/=N; R.b/=N; return R; } 5.8.4.2 Перегрузка методов Методы, которые имеют одинаковые имена, но отличаются списком параметров, называют перегруженными. Пример перегрузки методов сложения дробей class Droby { private: int a,b; int Nod(); public: Droby( int a1, int b1); Droby(){a=0;b=1;} void Add(Droby &d);//именится вызвавший объект //перегрузка метода Add:результат в вызваший объект void Add(Droby &d1, Droby &d2); inline void set_a( int a1){a=a1;} //подстановка в классе void set_b( int b1); //подстановка, реализация вне класса int get_a(){ return a;} int get_b(){ return b;} Droby& Sub(Droby &d); }; int main(){ Droby O1(2,3), O2(3,5); O1.Add(O2); Droby O3; O3.(O1,O2); } Компилятор по списку параметровотличит один метод от другого. 5.8.4.3 Функции – члены со спецификацией const Такая функция-член не может изменять объект, для которого она вызвана. Формат определения Заголовок функции-члена (параметры) |