
lect105-106.ppt
- Количество слайдов: 44
Лекции 5 - 6 Наследование, виды наследования. Режимы наследования. Переопределение методов. Обращение к конструкторам и деструкторам базовых классов. Обращение к переопределенным методам базовых классов. Множественное наследование и его проблемы. Виртуальное наследование.
Наследование представляет собой механизм повторного использования программного кода, в соответствии с которым новые классы создаются на основе существующих. Эти классы наследуют свойство и поведение базовых классов и приобретают дополнительно новые, необходимые для новых классов, качества. Новый класс называется производным.
Виды наследования n Простое (одиночное) наследование – производный класс получается на основе только одного базового класса. n Сложное (множественное) наследование – производный класс наследует свойства от многих классов. Основное предназначение наследования определяется возможностью добавлять, замещать и уточнять наследуемые от базовых классов свойства (атрибуты и методы).
Наследование Справедливы следующие высказывания: n Каждый объект производного класса является также объектом базового класса. n Объект базового класса не является объектами производных классов.
Наследование Описание класса, создаваемого на базе другого класса на языке С++ осуществляется следующим образом: class Имя. Класса: режим. Наследования Имя. Базового. Класса { //Описание секций класса с указанием //атрибутов и методов }; В языке С++ поддерживаются следующие режимы наследования: public – открытое наследование, protected – защищенное наследование, private – закрытое наследование.
Режимы наследования В базовом классе Вид наследования public protected private protected private - - -
Наследование class Base. Class{ private: int private_value; protected: int protected_value; public: int public_value; Base. Class() { private_value = 1; protected_value = 2; public_value = 3; } ~Base. Class() { } int get. Private() { return private_value; } int get. Protected() { return protected_value; } int get. Public() { return public_value; } };
Открытое наследование class Derived. Class : public Base. Class{ public: Derived. Class() { } ~Derived. Class() { } //void show. Private() { cout << private_value << endl; } Ошибка! void show. Private() { cout << get. Private() << endl; } void show. Protected() { cout << protected_value << endl; } void show. Public() { cout << public_value << endl; } }; int main(int argc, char* argv[]) { Derived. Class obj; cout << "Методы производного класса!n"; obj. show. Private(); //Вывод 1 obj. show. Protected(); //Вывод 2 obj. show. Public(); //Вывод 3 cout << "n. Методы базового класса!n"; cout << obj. get. Private() << endl; //Вывод 1 cout << obj. get. Protected() << endl; //Вывод 2 cout << obj. get. Public() << endl; //Вывод 3 cout << "n. Значения атрибутов!n"; //cout << obj. private_value << endl; Ошибка! //cout << obj. protected_value << endl; Ошибка! cout << obj. public_value << endl; //Вывод 3 return 0; }
Защищенное наследование class Derived. Class : protected Base. Class{ public: Derived. Class() { } ~Derived. Class() { } //void show. Private() { cout << private_value << endl; } Ошибка! void show. Private() { cout << get. Private() << endl; } void show. Protected() { cout << protected_value << endl; } void show. Public() { cout << public_value << endl; } }; int main(int argc, char* argv[]) { Derived. Class obj; cout << "Методы производного класса!n"; obj. show. Private(); //Вывод 1 obj. show. Protected(); //Вывод 2 obj. show. Public(); //Вывод 3 cout << "n. Методы базового класса!n"; //cout << obj. get. Private() << endl; Ошибка! //cout << obj. get. Protected() << endl; Ошибка! //cout << obj. get. Public() << endl; Ошибка! cout << "n. Значения атрибутов!n"; //cout << obj. private_value << endl; Ошибка! //cout << obj. protected_value << endl; Ошибка! //cout << obj. public_value << endl; Ошибка! return 0; }
Закрытое наследование class Derived. Class : private Base. Class{ public: Derived. Class() { } ~Derived. Class() { } //void show. Private() { cout << private_value << endl; } Ошибка! void show. Private() { cout << get. Private() << endl; } void show. Protected() { cout << protected_value << endl; } void show. Public() { cout << public_value << endl; } }; int main(int argc, char* argv[]) { Derived. Class obj; cout << "Методы производного класса!n"; obj. show. Private(); //Вывод 1 obj. show. Protected(); //Вывод 2 obj. show. Public(); //Вывод 3 cout << "n. Методы базового класса!n"; //cout << obj. get. Private() << endl; Ошибка! //cout << obj. get. Protected() << endl; Ошибка! //cout << obj. get. Public() << endl; Ошибка! cout << "n. Значения атрибутов!n"; //cout << obj. private_value << endl; Ошибка! //cout << obj. protected_value << endl; Ошибка! //cout << obj. public_value << endl; Ошибка! return 0; }
Закрытое наследование class A{ public: int value; A() { value = 1; } }; class B: private A{ public: int get. Val. B() { return value; } }; class C: public B{ public: //int get. Val. C 1() { return value; } Ошибка! int get. Val. C 2() { return get. Val. B(); } };
Вызов конструкторов базового класса В языке С++ в реализации конструктора можно в явном виде вызывать конструктор базового класса: Имя-класса(параметры) : Имя-базового-класса(параметры) { /* тело конструктора */ } В неявном виде вызывается конструктор по умолчанию базового класса. Если он не определено, то это приводит к ошибке компиляции программы.
Вызов конструкторов базового класса class Base. Class{ protected: int base_value; public: Base. Class(int); ~Base. Class(); int get. Base. Value(); }; class Derived. Class : public Base. Class{ protected: int derived_value; public: Derived. Class(int, int); ~Derived. Class(); int get. Derived. Value(); };
Вызов конструкторов базового класса Base. Class: : Base. Class(int value) { base_value = value; } Base. Class: : ~Base. Class(void) { } int Base. Class: : get. Base. Value() { return base_value; } Derived. Class: : Derived. Class(int value, int base): Base. Class(base) { derived_value = value; } Derived. Class: : ~Derived. Class() { } int Derived. Class: : get. Derived. Value() { return derived_value; }
Вызов конструкторов базового класса (ошибочный код) Base. Class: : Base. Class(int value) { base_value = value; } Base. Class: : ~Base. Class(void) { } int Base. Class: : get. Base. Value() { return base_value; } Derived. Class: : Derived. Class(int value, int base) { derived_value = value; base_value = base; } Derived. Class: : ~Derived. Class() { } int Derived. Class: : get. Derived. Value() { return derived_value; }
Вызов конструкторов базового класса int main(int argc, char* argv[]) { Derived. Class obj(5, 10); cout << "Базовый: " << obj. get. Base. Value() << endl; cout << "Производный: " << obj. get. Derived. Value() << endl; return 0; }
Порядок вызова конструкторов и деструкторов при наследовании При создании объектов производных классов в языке С++ для них сначала вызываются конструкторы базовых классов, а затем конструкторы производных классов. При уничтожении объектов наоборот: сначала вызываются деструкторы производных классов, а затем – деструкторы базовых классов.
Порядок вызова конструкторов и деструкторов при наследовании class Base. Class{ public: Base. Class() { cout << “Конструктор базового классаn”; } ~Base. Class() { cout << “Деструктор базового классаn”; } }; class Derived. Class : public Base. Class{ public: Derived. Class() { cout << “Конструктор производного классаn”; } ~Derived. Class() { cout << “Деструктор производного классаn”; } }; int main(int argc, char* argv[]) { Derived. Class obj; return 0; } На экране: Конструктор базового класса Конструктор производного класса Деструктор базового класса
Переопределение методов class Base. Class{ public: void print() { cout << “Метод базового класса!n” << endl; } }; class Derived. Class : public Base. Class{ public: void print() { cout << “Метод производного класса!n” << endl; } }; int main(int argc, char* argv[]) { Base. Class base; Derived. Class derived; base. print(); //Метод базового класса! derived. print(); //Метод производного класса! derived. Base. Class: : print(); //Метод базового класса! return 0; }
Переопределение атрибутов class Base. Class{ protected: int value; public: Base. Class(int val) { value = val; } ~Base. Class() { } void print() { cout << “Значение базового класса: “ << value << endl; } }; class Derived. Class : public Base. Class{ protected: int value; public: Derived. Class(int base, int val) : Base. Class(base) { value = val; } ~Derived. Class() { } void print() { cout << “Значение производного класса: “ << value << endl; } void print. Base() { cout << “Значение базового класса: “ << Base. Class: : value << endl; } };
Переопределение атрибутов int main(int argc, char* argv[]) { Base. Class base(10); Derived. Class derived(15, 20); base. print(); //Значение базового класса: 10 derived. print(); derived. Base. Class: : print(); derived. print. Base(); //Значение производного класса: 20 //Значение базового класса: 15 return 0; }
Присвоение значений объектов В языке С++ можно присваивать значению объекта базового класса значение объекта производного класса. Обратное преобразование не допускается. Derived. Class obj(10, 20); Base. Class base_obj(15); base_obj = obj; obj. print(); base_obj. print(); Derived. Class new_obj(1, 2); new_obj = base_obj; new_obj = (Derived. Class)base_obj; new_obj. print(); //Ошибка!
Присвоение указателей В языке С+ можно присваивать указателю на базовый класс указатель на производный класс. Обратное присвоение возможно, но с явным приведением типа. Derived. Class *obj = new Derived. Class(10, 20); Base. Class *base_obj = obj; obj->print(); base_obj->print(); //Значение производного класса: 20 //Значение базового класса: 10 Derived. Class *new_obj = (Derived. Class *)base_obj; new_obj->print(); //Значение производного класса: 20 delete obj; base_obj = new Base. Class(10); new_obj = (Derived. Class *)base_obj; new_obj->print(); delete base_obj; //Неправильное преобразование //? ? ?
Иерархия классов
Пример class Item{ public: Item(); ~Item(); bool Is. Taken() const; // истина, если единица хранения на руках bool Is. Available() const; // истина, если этот предмет имеется в библиотеке long Get. Inv. Number() const; // инвентарный номер void Take(); // операция "взять" void Return(); // операция "вернуть" private: long inv. Number; // инвентарный номер — целое число bool taken; // хранит состояние объекта - взят на руки };
Пример void Take. An. Item(Item& i) // выдать на руки {. . . if (i. Is. Available()) i. Take(); }
Пример class Book : public Item { public: String Author(void) const; String Title(void) const; String Publisher(void) const; long Year. Of. Publishing(void) const; String Reference(void) const; //Полная ссылка на книгу private: String author; // автор String title; // название String publisher; // издательство short year; // год выпуска };
Пример class Magazine : public Item { public: String Volume(void) const; short Number(void) const; String Title(void) const; Date. Of. Issue() const; private: String volume; // том short number; // номер String title; // название Date date; // дата выпуска };
Пример Book b; long in = b. Get. Inv. Number(); String t = b. Reference(); String Book: : Reference(void) const { String result = author + "n" + title + "n" + String(Get. Inv. Number()); } return result;
Пример class Item{. . . protected: long inv. Number; }; String Book: : Reference(void) const { String result = author + "n" + title + "n" + String(inv. Number); return result; }
Перекрытие методов class A { public: . . . int foo(); . . . }; class B : public A { public: int foo(); void bar(); }; void. B: : bar(){ x = foo(); // вызывается метод foo класса B x = A: : foo(); // вызывается метод foo класса А }
Виртуальные методы class Item { public: virtual String Name(void) const; . . . }; class Book : public Item { public: virtual String Name(void) const; . . . }; class Magazine : public Item { public: virtual String Name(void) const; . . . }; String Item: : Name(void) const { return ""; } String Book: : Name(void) const { return author + title + publisher + String(year); } String Magazine: : Name(void) const { return title + String(year) + String(number); }
Виртуальные методы Item* ptr; . . . String name = ptr->Name(); Item* ptr; . . . if (type == "Book") ptr = new Book; else if (type == "Magazine") ptr = new Magazine; . . . String name = ptr->Name();
Виртуальные методы class Shape { public: Shape(); virtual void Draw(void); }; // квадрат class Square : public Shape { public: Square(); virtual void Draw(void); private: double length; // длина стороны }; // круг class Circle : public Shape { public: Circle(); virtual void Draw(void); private: short radius; //радиус }; Repaint(Shape* shape) { shape->Draw(); }
Виртуальные методы и переопределение методов func(Item item) { item. Name(); // вызывается метод Item: : Name() } func 1(Item& item) { item. Name(); // вызывается метод в соответствии // с типом того объекта, на который // ссылается item }
Преобразование базового и производного типа Circle* p. C; . . . Shape* p. Shape = p. C; Item* i. Ptr; . . . Book* b. Ptr = (Book*)i. Ptr; //Опасное преобразование
Внутреннее и защищенное наследование class B : private A {. . . }; class A { public: int foo(); }; class С : protected A {. . . }; B b; b. foo(); //недопустимо class B: private A { int m() { foo(); //недопустимо } };
Абстрактные классы class Item { public: . . . virtual String Name() const = 0; }; Item it; //Ошибка Item* itptr = new Item; //Ошибка Book b; Item* itptr = &b; Item& itref = b;
Абстрактные классы class A { public: virtual ~A() = 0; }; A: : ~A() {. . . }
Множественное наследование
Множественное наследование class Annotation { public: String Get. Text(void); private: String annotation; }; class Shape { public: virtual void Draw(void); }; class Annotated. Square : public Shape, public Annotation { public: virtual void Draw(); };
Множественное наследование 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(); //Неоднозначность cp->A: : fun(); cp->B: : fun();
Множественное наследование class Person { public: String name(); }; class Student : public Person {. . . }; class Librarian : public Person {. . . }; class Student. Librarian : public Student, public Librarian { }; Student. Librarian* sp; sp->Person: : name(); //ошибка // правильное обращение sp->Student: : Person: : name();
Виртуальное наследование class Student : virtual Person { }; class Librarian : virtual Person { }; class Student. Librarian : public Student, public Librarian { }; Student. Librarian* sp; sp->Person: : name();