Виртуални функции

Подобни документи
Lush Green

Lush Green

2. Наследяване в C++ Съдържание Съдържание Наследяване (Rev: 1.2) Любомир Чорбаджиев 1 1 февруари 2007 г. 1 Наследяване 1 2

Динамична памет. Конструктори и деструктори Любомир Чорбаджиев Технологическо училище Електронни системи Технически университет, София

4

Предефиниране на оператори. Копиращ конструктор. Оператор за присвояване Любомир Чорбаджиев Технологическо училище Електронни системи Технически униве

Динамична памет Трифон Трифонов Увод в програмирането, спец. Компютърни науки, 1 поток, спец. Софтуерно инженерство, 2016/17 г. 21 декември 2016 г. Тр

Дефиниране на шаблон Шаблони (Templates) Любомир Чорбаджиев Технологическо училище Електронни системи Технически университет, София

Обработка на грешки Изключения Любомир Чорбаджиев Технологическо училище Електронни системи Технически университет, София Re

Black and White

Класове в C++ (Rev: 742) Любомир Чорбаджиев 1 20 октомври 2006 г. Съдържание Съдържание 1 Обектно-ориентирано програмиране 1

Канонична форма на клас или 4 (голямата четворка) Трифон Трифонов Обектно-ориентирано програмиране, спец. Компютърни науки, 1 поток, спец. Софтуерно и

Структура на програма в C - Част 7 - масиви, оператор за индексиране, sizeof оператор

C++

Синтаксис за дефиниране на функции Трифон Трифонов Функционално програмиране, спец. Информатика, 2015/16 г. 6 януари 2016 г. Трифон Трифонов (ФП-И 15/

ИНТЕРНЕТ ПРОГРАМИРАНЕ - JAVA JAVA ОБЕКТИ Ненко Табаков Пламен Танов Технологическо училище Електронни системи Технически университет София 9 октомври

Структура на програма в C - Част 6 - goto, switch, break и continue клаузи

Указатели. Маисиви, указатели, параметри на функции Калин Георгиев 21 декември 2016 г. Калин Георгиев Увод в програмирането 21 декември 2016 г. 1 / 23

-

Програмиране на Паскал

СЪЗДАВАНЕ НА СЪДЪРЖАНИЕ, ИНДЕКСЕН УКАЗАТЕЛ И ВМЪКВАНЕ НА ПОЛЕТА I. СЪЗДАВАНЕ НА СЪДЪРЖАНИЕ Съдържанието се създава по съществуващ в Word форматен стил

При изпълнението на програма се извършват определени действия над данните, дефинирани в програмата.тези данни могат да бъдат постоянни ( константи ) и

Microsoft Word - VM22 SEC55.doc

3. Синтактичен анализ. Граматика на учебен програмен език STUDENT. Извеждане на изречения от правилата на граматиката Цел на упражнението Упражнението

Сериализация Калин Георгиев 13 май 2016 г. Калин Георгиев Обектно ориентирано програмиране 13 май 2016 г. 1 / 23

2. Лексически анализ. Основни понятия и алгоритъм на лексическия анализ. Програмна структура на лексическия анализатор Цел на упражнението Упражнениет

Microsoft Word - PRMAT sec99.doc

Mathematica CalcCenter

Масиви и низове Трифон Трифонов Увод в програмирането, спец. Компютърни науки, 1 поток, 2018/19 г. 15 ноември 6 декември 2018 г. Трифон Трифонов (УП 1

Структура на програма в C - Част 2 - типове, функции

Масиви и низове Трифон Трифонов Увод в програмирането, спец. Компютърни науки, 1 поток, 2018/19 г ноември 2018 г. Трифон Трифонов (УП 18/19) Ма

ПРОГРАМНО ОСИГУРЯВАНЕ НА КОМПЮТЪРА

МИНИСТЕРСТВО НА ОБРАЗОВАНИЕТО И НАУКАТА

doll Механична кукла Механичните кукли автоматично повтарят предварително зададена последователност от движения. В Япония има традиции в изработката н

Проф

Структура на програма в C - Част 9 - низове от символи, C-string

Microsoft Word - Glava24.doc

Проект 1: Форма за решаване на тест Създайте приложение, което тества знанията на ученика по Информатика. Върху формата да се разположат въпроси с по

ЕКСПЛОДИРАЩИ ТОЧКИ ГЛАВА 1 МАШИНИ Добре дошли на борда на нашето приключение. Това е математическо приключение базирано върху една моя история (аз съм

Slide 1

В тази част, ще разгледаме аритметичните и логически операции, както, и включването им в изрази. В следващата таблица са дадени всички възможни операц

HTML - списъци

Microsoft Word - Primer3_1.doc

Препис:

Виртуални функции

Статично свързване Как компилаторът избира кой метод или коя функция да бъде извикана? Прави се сравнение между формални и фактически параметри и се избира най-точното съвпадение в случай, че има няколко най-точни, грешка за двусмислица Важно: Методът, който ще се извика се предопределя по време на компилация и при всяко изпълнение е един и същ

Статично свързване Пример: Person* pp = new Student( Иван, 123, 40000, 5); Кой метод ще извика pp->print()? Student::print или Person::print? подсказка: кое от двете може да се определи със сигурност по време на компилация, а не по време на изпълнение? Свързването по време на компилация нариаме статично или ранно (early binding) В C++ по подразбиране свързването е статично има езици, в които по подразбиране не е статично!

Защо само статично свързване не стига Пример: Person* pp = NULL; char c; cin >> c; if (c == 's') pp = new Student; if (c == 'e') pp = new Employee;... if (pp!= NULL) pp->print(); // Person::print Няма как компилаторът да знае какво ще въведе потребителят, затова се залага на сигурното Как можем да направим така, че да се извика този метод, който трябва?

Защо само статично свързване не стига Решение 1: Person* pp = NULL; char c; cin >> c; if (c == 's') pp = new Student; if (c == 'e') pp = new Employee;... if (pp!= NULL) { if (c == 's') ((Student*)pp)->print(); if (c == 'e') ((Employee*)pp)->print();

Защо само статично свързване не стига Решение 2: struct SmartPerson { Person* person; enum { PERSON, STUDENT, EMPLOYEE } type; void print() const { if (type == PERSON) person->print(); if (type == STUDENT) ((Student const*)person)->print(); if (type == EMPLOYEE) ((Employee const*)person)->print(); } }; SmartPerson pp = { NULL, PERSON }; char c; cin >> c; if (c == 's') { pp.person = new Student; pp.type = STUDENT; } if (c == 'e') { pp.person = new Employee; pp.type = EMPLOYEE; } pp.print();

Защо само статично свързване не стига И двете решения не са напълно добри, понеже изискват програмата да помни допълнителни неща... Колко хубаво би било, ако можеше със създаването си обектът да има етикет и по време на изпълнение етикетът се използва, за да се определи кой метод да се извика

Динамично свързване При динамичното (късно) свързване (late binding) методът, който ще се извика, се определя по време на изпълнение извиква се методът на този клас, от който всъщност е даденият обект независимо че указателят може да е дефиниран към базов клас

Виртуални член-функции В C++ динамичното свързване може да се включи за всяка отделна член-функция, като тя се обяви като виртуална virtual <сигнатура>; Класове с виртуални функции се наричат полиморфни Примери: class Person {... virtual void print() const;... }; Person p, *pp = &p; pp->print(); // Person::print() Student s; pp = &s; pp->print(); // Student::print() Employee e; pp = &e; pp->print(); // Employee::print()

Особености на виртуалните функции Само член-функции могат да бъдат виртуални Конструкторите не могат да са виртуални те се извикват преди обектът да е създаден Статичните член-функции не могат да са виртуални те могат да се извикват без обект Наследяващата член-функция в производния клас трябва да е със същата сигнатура ако сигнатурата е различна, това е друга функция наследяващите функции са автоматично виртуални и запазената дума virtual може да се пропусне virtual се пише само пред декларацията, не пред дефиницията

Видимост на виртуални функции Правило: Видимостта на една виртуална функция се определя от видимостта ѝ в класа на обекта, (указателя, псевдонима), през който се извиква Това означава ли, че: може private виртуална функция да се извика извън класа? няма смисъл виртуална функция в основния клас да е private или protected, понеже няма как да се извика? основният клас, който съдържа виртуалната функция, трябва да е наследен с public?

Извикване на виртуални функции Какво става ако виртуална функция, се извика: чрез обект Person p; p.print(); статично свързване, понеже типът се знае предварително чрез указател Person* pp = &s; pp->print(); динамично свързване чрез псевдоним Person& ap = e; ap.print(); еквивалентно на указател, динамично свързване чрез указване на област Person::print(); статично свързване, указали сме кой метод да се извика

Извикване на виртуални функции Какво става ако виртуална функция, се извика: от член-функция void Person::f() {... print();... } еквивалентно на извикване през this, динамично свързване! от конструктор на основен клас Person::Person() { print(); } статично свързване, обектът от производен клас още не е построен! от деструктор на основен клас Person::~Person() { print(); } статично свързване, обектът от производния клас вече е разрушен!

Косвено динамично свързване void Person::prettyPrint() const { cout << ------------- [ Person ] ------------- ; print(); cout << ---------------------------------------- ; } Ако Student s; какво ще изведат: Person p = s; p.prettyprint(); Person* pp = &s; pp->prettyprint(); Person& ap = s; ap.prettyprint();

Косвено динамично свързване void Person::prettyPrint() const { cout << ------------- [ Person ] ------------- ; print(); cout << ---------------------------------------- ; } Ако Student s; какво ще изведат: Person p = s; p.prettyprint(); Person* pp = &s; pp->prettyprint(); Person& ap = s; ap.prettyprint(); Извод: Виртуалността автоматично се разпростира и сред член-функциите, които извикват виртуални член-функции!

Коя реална функция ще се извика? Не е задължително виртуална функция да има нова реализация във всеки производен клас Избира се виртуалната функция, която е най-близко до класа, от който е обекта final overrider търси се отдолу-нагоре При множествено наследяване могат да се получат двусмислици ако Intern не дефинираше print(), какво щеше да изведе следният код? Intern i; Person* pp = &i; pp->print(); двусмислицата се вижда още по време на компилация!

Механизъм на виртуалните таблици Как програмата решава по време на изпълнение кой метод да се изпълни? За всеки клас с виртуални функции се създава таблица с указатели към тях (виртуална таблица) За всеки обект с виртуални функции в началото се поставя указател към виртуална таблица При динамично свързване, се случва следното: компилаторът изчислява номера i на извикваната виртуалната функция компилаторът генерира код, който намира i-тия указател във виртуалната таблица на обекта извиква функцията, която се сочи от този указател

Механизъм на виртуалните таблици Employee Employee::Employee Employee::setPosition Employee::print Employee::getPosition Person Person::Person Person::setName Person::print Person::getID Student Student::Student Student::setGrade Student::print Student::getFN......... Employee VTable Person VTable Student VTable Person vptr name id Employee Person vptr name id position salary

Виртуални таблици и множествено наследяване При множествено наследяване се създава по една виртуална таблица за всеки основен клас Във всеки обект има по един указател към виртуална таблица за всеки основен клас Ако имаме и виртуално наследяване, представянето става още по-сложно За щастие, в рамките на този курс няма да пишем компилатор за C++

Типова информация по време на изпълнение (RTTI) В C++ има механизъм за намиране на типа на даден обект по време на изпълнение typeid(<израз>) връща обект от тип type_info ако <израз> е lvalue от полиморфен клас, връща динамичния тип на <израз> иначе, връща статичния тип на <израз> можете да получите името на даден тип cout << typeid(pp).name() << ' ' << typeid(*pp).name(); два типа могат да се сравняват с == или!= typeid(p)!= typeid(s), typeid(*pp) == typeid(student)

Виртуални деструктори Person* pp = new Employee;... delete pp; Кой деструктор ще се извика?

Виртуални деструктори Person* pp = new Employee;... delete pp; Кой деструктор ще се извика? статично свързване, деструкторът на Person динамичната памет на Employee остава неосвободена (изтичане на памет)! Искаме да се вика правилният деструктор! Можем да декларираме деструктора като виртуален Тогава свързването е динамично и ще се извика деструкторът на Employee