Скачать презентацию Иерархии классов Степанюк Константин Сергеевич 24 const gmail com Скачать презентацию Иерархии классов Степанюк Константин Сергеевич 24 const gmail com

#6 Иерархии классов.pptx

  • Количество слайдов: 23

Иерархии классов Степанюк Константин Сергеевич 24 const@gmail. com Иерархии классов Степанюк Константин Сергеевич 24 const@gmail. com

class Char. List { public: 1. Char. List(int size); ~Char. List(); 2. Char. List class Char. List { public: 1. Char. List(int size); ~Char. List(); 2. Char. List & operator = (const Char. List & other); private: char* data; 3. }; Char. List: : Char. List(int size) { data = new char[256]; } 4. Char. List: : ~Char. List() { if (data != NULL) { delete data; 5. } } Char. List& Char. List: : operator = (const Char. List & other) { int size = std: : strlen(other. data); data = new char[size]; std: : copy(other. data, other. data + size, data); return *this; } Фиксированное число 256 вместо переменной size Проверка на NULL перед оператором delete Удаление объекта (delete) вместо удаления массива (delete[]) Отсутствие проверки на самоприсваивание При копировании не освобождается «старая» память

Char. List: : Char. List(int size) { data = new char[size]; } Char. List: Char. List: : Char. List(int size) { data = new char[size]; } Char. List: : ~Char. List() { delete[] data; } Char. List& Char. List: : operator = (const Char. List & other) { if (this != &other) { // защита от неправильного самоприсваивания // 1: освобождаем "старую" память delete [] data; // 2. ищем размер строки int size = std: : strlen(other. data); // 3: выделяем "новую" память и копируем элементы data = new char[size]; std: : copy(other. data, other. data + size, data); } } // по соглашению всегда возвращаем *this return *this;

Традиционный подход (стиль C) Проблема –автоматизация предприятия, информация о сотрудниках struct Employee { string Традиционный подход (стиль C) Проблема –автоматизация предприятия, информация о сотрудниках struct Employee { string first. Name, family. Name; Date hiring. Date; short department; //… }; struct Manager { Employee emp; //Информация о менеджере как сотруднике set team; short level; };

Проблемы традиционного подхода • Концептуально Менеджер является сотрудником, но с точки зрения компилятора между Проблемы традиционного подхода • Концептуально Менеджер является сотрудником, но с точки зрения компилятора между Employee и Manager нет общности • Manager* не является Employee*, нельзя использовать Manager* там где используется Employee* • Manager* нельзя напрямую положить в список или множество объектов Employee* • Решение –механизм наследования с возможностью явного выражения общности между абстракциями

Стиль С++ -наследование struct Manager : public Employee { set<Employee*> team; short level; //… Стиль С++ -наследование struct Manager : public Employee { set team; short level; //… }; • Структура Manager наследует все свойства структуры Employee, возможно преобразование типов от Manager к Employee, Manager* к Employee* и Manager& к Employee& void f (Manager m, Employee e) { list elist; elist. push_front( &m ); elist. push_front( &e ); }

Использование –основные концепции • Наследование – основной механизм ООП: – Уточнение (переопределение поведения –перегрузка) Использование –основные концепции • Наследование – основной механизм ООП: – Уточнение (переопределение поведения –перегрузка) – Расширение (интерфейса абстракции) – Переиспользование (использование ранее написанного кода, иногда, в данном случае, лучше воспользоваться агрегацией) • Уточнение и расширение – базис в направлениях которого идут изменения в классах • LSP(Liskov substitution principle): функция, принимающая в качестве аргумента объект базового класса должна уметь работать с объектами любых открытых производных классов

Уточнение - пример class Employee { //…. public: string full. Name() { return first. Уточнение - пример class Employee { //…. public: string full. Name() { return first. Name+ “ “+last_name; } void print( ostream& ostr) { ostr<<“Name: ”<

Расширение –пример class Manager: public Employee { set<Employee*> emp; short level; public: void print. Расширение –пример class Manager: public Employee { set emp; short level; public: void print. Team( ostream& ostr) { print(ostr); ostr<<“ team: ”<: : iterator i = emp. begin(); i != emp. end(); i++ ) { *i->print(ostr); ostr<

Виртуальный полиморфизм – постановка • Проблема – есть список сотрудников, среди которых есть менеджеры. Виртуальный полиморфизм – постановка • Проблема – есть список сотрудников, среди которых есть менеджеры. Необходимо распечатать информацию о каждом сотруднике с учетом того, что он может быть менеджером //Напрямую –не работает listelist = …; for (list: : iterator i = elist. begin(); i!=elist. end(); ++i ) { //Всегда будет вызываться Employee: : print //но для менеджеров нужно вызывать Manager: : print *i->print(ostr); }

Альтернативы • Гарантировать что список содержит только элементы одного типа (Employee* либо Manager*) – Альтернативы • Гарантировать что список содержит только элементы одного типа (Employee* либо Manager*) – трудно достичь • Поместить поле о типе (поле типа) в базовый класс, чтобы заинтересованные функции могли его проверять enum Emp. Type {E, M}; //Что делать если потребуется расширение? class Employee { Emp. Type employee. Type; public: Emp. Type get. Type () {return employee. Type; } }; • Использовать динамическое преобразование типа dynamic_cast • Сделать функцию print виртуальной

Виртуальный полиморфизм – решение class Employee { //… public: virtual void print(ostr& os) {…} Виртуальный полиморфизм – решение class Employee { //… public: virtual void print(ostr& os) {…} }; class Manager : public Employee{ //… public: virtual void print(ostr& os) {…} }; list elist = …; for (list: : iterator i = elist. begin(); i != elist. end(); i++ ) { (*i)->print(ostr); //Employee: : print либо Manager: : print }

Реализация operator<< ostream& operator<< (ostream& o, const Employee &emp) { emp. print( o ); Реализация operator<< ostream& operator<< (ostream& o, const Employee &emp) { emp. print( o ); return o; } listelist = …; for (list: : iterator it = elist. begin(); it != elist. end(); ++it) { cout<< *(*it) << endl; }

Использование виртуальных функций • В случае адресации объекта по ссылке или указателю осуществляется вызов Использование виртуальных функций • В случае адресации объекта по ссылке или указателю осуществляется вызов виртуальной функции соответствующей реальному типу объекта • В случае использования обычных переменных происходит усечение при преобразовании типа и будет осуществлен вызов функции, соответствующей типу переменной Manager *mp = …; Manager m = …; Employee *pemp = mp; Employee &remp = m; Employee emp = m; pemp->print(std: : cout); remp. print(std: : cout); //Объект имеет тип Manager //Внимание –усечение!!! // Вызов Manager: : print // Вызов Employee: : print

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

Виртуальные функции и конструкторы • При вызове конструктора или деструктора базового класса при создании Виртуальные функции и конструкторы • При вызове конструктора или деструктора базового класса при создании (уничтожении) объекта производного класса указатель на таблицу виртуальных функций указывает на таблицу этого базового класса • Таким образом виртуальные функции не рассматриваются как виртуальные при вызове из конструкторов или деструкторов (ведут себя как обычные функции объявленные в классе) • Избегайте вызова виртуальных функций в конструкторах и деструкторах

Виртуальные конструкторы Виртуальных конструкторов нет Эквивалентного поведения можно добиться с помощью виртуальных функций для Виртуальные конструкторы Виртуальных конструкторов нет Эквивалентного поведения можно добиться с помощью виртуальных функций для создания объектов class Base { public: virtual Base* clone () const { return new Base(*this); } }; class Derived: public Base { public: //Именно Derived*!, компилятор учитывает, что Derived //наследник Base при такой перегрузке виртуальной ф-ии virtual Derived* clone () const { return new Derived(*this); } }; • •

Базовый класс class Base { A field, *pointer; public: Base(); //Конструктор по умолчанию Base(const Базовый класс class Base { A field, *pointer; public: Base(); //Конструктор по умолчанию Base(const Base&); //Конструктор копии Base& operator= (const Base&); virtual ~Base(); //Виртуальный деструктор!!! }; Base: : Base(): field(0){ pointer = new A(); } Base: : Base(const Base& m): field(m. field) { pointer = new A(*(m. pointer)); } Base: : ~Base() { delete pointer; }

Базовый класс const Base& Base: : operator = (const Base& m) { //Проверка на Базовый класс const Base& Base: : operator = (const Base& m) { //Проверка на присваивание самому себе if (this != &m) { delete pointer; //Высвобождение указателя field = m. field; pointer = new A(*(m. pointer)); } return *this; } const Base& Base: : operator = (const Base& m) { if (this != &m) { field = m. field; (*pointer) = *(m. pointer); } return *this; }

Производный класс class Derived : public Base { Cls *pc; public: Derived(); Derived(const Derived&); Производный класс class Derived : public Base { Cls *pc; public: Derived(); Derived(const Derived&); virtual ~Derived(); //Виртуальный! const Derived& operator = (cosnt Derived&); }; Derived: : Derived(): Base() { pc = new Cls(); } Derived: : Derived(const Derived &r): Base(r) { pc = new Cls(*(r. pc)); }

Производный класс const Derived& Derived: : operator=(const Derived &r) { if (this != &r) Производный класс const Derived& Derived: : operator=(const Derived &r) { if (this != &r) { *( static_cast(this)) = r; delete pc; pc = new Cls(*(r. pc)); } return *this; } Derived: : ~Derived() { delete pc; }

Управление доступом • Спецификатор доступа к базовому классу управляет доступом к членам базового класса Управление доступом • Спецификатор доступа к базовому классу управляет доступом к членам базового класса и возможностью преобразования объектов, указателей и ссылок из типа производного класса в тип базового класса class X: public B {…} class Y: protected B{…} class Z: private B{…} • Закрытого наследования следует избегать, вместо него в подавляющем большинстве случаев лучше воспользоваться агрегацией