runtime polymorphism c
Детаљна студија полиморфизма током извођења у Ц ++.
Полиморфизам током извођења познат је и као динамички полиморфизам или касно везивање. У полиморфизму извршавања, позив функције се разрешава у току извођења.
Супротно томе, за компајлирање времена или статичког полиморфизма, компајлер изводи објекат у време извођења, а затим одлучује који ће се позив функције везати за објекат. У Ц ++, рунтиме полиморфизам се имплементира применом методе која замењује.
У овом упутству детаљно ћемо истражити све о рунтим полиморфизму.
=> Овде погледајте СВЕ туторијале за Ц ++.
Шта ћете научити:
- Замена функције
- Виртуална функција
- Рад виртуелне табеле и _вптр
- Чисте виртуелне функције и апстрактни час
- Виртуелни деструктори
- Закључак
- Препоручено читање
Замена функције
Замена функције је механизам помоћу којег се функција дефинисана у основној класи поново дефинише у изведеној класи. У овом случају кажемо да је функција замењена у изведеној класи.
Треба имати на уму да се замена функције не може извршити унутар класе. Функција је замењена само у изведеној класи. Стога наследство треба да буде присутно за надјачавање функције.
Друга ствар је да функција из основне класе коју надјачавамо треба да има исти потпис или прототип, тј. Требало би да има исто име, исти тип повратка и исту листу аргумената.
Погледајмо пример који показује надјачавање метода.
#include using namespace std; class Base { public: void show_val() { cout << 'Class::Base'< Излаз:
Класа :: База
Класа :: Изведено
У горњем програму имамо основну и изведену класу. У основној класи имамо функцију схов_вал која је замењена у изведеној класи. У главној функцији креирамо сваки објекат класе Басе и Деривед и позивамо функцију схов_вал са сваким објектом. Производи жељени излаз.
Горње везивање функција помоћу објеката сваке класе је пример статичког везивања.
Сада да видимо шта се дешава када користимо показивач основне класе и додамо изведене објекте класе као његов садржај.
Пример програма приказан је испод:
#include using namespace std; class Base { public: void show_val() { cout << 'Class::Base'; } }; class Derived:public Base { public: void show_val() //overridden function { cout <<'Class::Derived'; } }; int main() { Base* b; //Base class pointer Derived d; //Derived class object b = &d; b->show_val(); //Early Binding }
Излаз:
Класа :: База
Сада видимо, да је излаз „Цласс :: Басе“. Дакле, без обзира на тип објекта који основни показивач држи, програм приказује садржај функције класе чији је основни показивач тип. У овом случају се врши и статичко повезивање.
Да би основни показивач био излаз, исправан садржај и правилно повезивање, идемо на динамичко везивање функција. То се постиже коришћењем механизма виртуелних функција који је објашњен у следећем одељку.
Виртуална функција
Како би надјачана функција требала бити динамички везана за тело функције, основну функцију класе чинимо виртуелном помоћу кључне речи „виртуал“. Ова виртуелна функција је функција која је надјачана у изведеној класи и компајлер врши касно или динамичко везивање за ову функцију.
Сада модификујмо горњи програм тако да укључује виртуелну кључну реч на следећи начин:
#include using namespace std;. class Base { public: virtual void show_val() { cout << 'Class::Base'; } }; class Derived:public Base { public: void show_val() { cout <<'Class::Derived'; } }; int main() { Base* b; //Base class pointer Derived d; //Derived class object b = &d; b->show_val(); //late Binding }
Излаз:
Класа :: Изведено
Дакле, у горњој дефиницији класе Басе, направили смо функцију схов_вал као „виртуелну“. Како се функција основне класе чини виртуелном, када додељујемо изведени објекат класе показивачу основне класе и позивамо функцију схов_вал, везивање се дешава током извршавања.
Дакле, како показивач основне класе садржи изведени објект класе, тело функције схов_вал у изведеној класи везано је за функцију схов_вал и отуда излаз.
У Ц ++-у, замењена функција у изведеној класи такође може бити приватна. Компајлер проверава тип објекта само у време компајлирања и веже функцију у време извођења, стога не прави никакву разлику чак и ако је функција јавна или приватна.
Имајте на уму да ако је функција проглашена виртуелном у основној класи, тада ће бити виртуелна у свим изведеним класама.
Али до сада нисмо разговарали о томе како тачно виртуелне функције играју улогу у идентификовању исправних функција које треба везати или другим речима, како се заправо догађа касно везивање.
Виртуелна функција је тачно везана за тело функције током извођења помоћу концепта виртуелна табела (ВТАБЛЕ) и скривени показивач зван _вптр.
Оба ова концепта су интерна имплементација и програм их не може користити директно.
Рад виртуелне табеле и _вптр
Прво, схватимо шта је виртуелна табела (ВТАБЛЕ).
Компајлер у време компајлирања поставља по један ВТАБЛЕ за класу која има виртуелне функције, као и класе изведене из класа које имају виртуелне функције.
ВТАБЛЕ садржи уносе који су показивачи функција на виртуелне функције које могу позвати објекти класе. За сваку виртуелну функцију постоји један унос показивача функције.
У случају чистих виртуелних функција, овај унос је НУЛЛ. (То је разлог зашто не можемо да направимо инстанцу апстрактне класе).
Следећи ентитет, _вптр који се назива втабле показивач је скривени показивач који компајлер додаје основној класи. Овај _вптр указује на втабле класе. Све класе изведене из ове основне класе наслеђују _вптр.
Сваки објекат класе који садржи виртуелне функције интерно чува овај _вптр и прегледан је за корисника. Сваки позив виртуелној функцији помоћу објекта се затим разрешава помоћу овог _вптр.
Узмимо пример за демонстрацију рада втабле и _втр.
#include using namespace std; class Base_virtual { public: virtual void function1_virtual() {cout<<'Base :: function1_virtual()
';}; virtual void function2_virtual() {cout<<'Base :: function2_virtual()
';}; virtual ~Base_virtual(){}; }; class Derived1_virtual: public Base_virtual { public: ~Derived1_virtual(){}; virtual void function1_virtual() { coutfunction2_virtual(); delete (b); return (0); }
Излаз:
Изведено1_виртуал :: фунцтион1_виртуал ()
База :: фунцтион2_виртуал ()
У горњем програму имамо основну класу са две виртуелне функције и виртуелним деструктором. Такође смо извели класу из основне класе и у томе; надјачали смо само једну виртуелну функцију. У главној функцији изведени показивач класе додељује се основном показивачу.
Тада позивамо обе виртуелне функције помоћу показивача основне класе. Видимо да се надјачана функција позива када се позове, а не основна функција. Док се у другом случају, с обзиром да функција није надјачана, позива функција основне класе.
Сада да видимо како је горњи програм представљен интерно помоћу втабле и _вптр.
Према ранијем објашњењу, будући да постоје две класе са виртуелним функцијама, имат ћемо две втабле - по једну за сваку класу. Такође, _вптр ће бити присутан за основну класу.

Изнад је приказан сликовни приказ како ће изглед втабле-а бити за горњи програм. Втабле за основну класу је једноставан. У случају изведене класе, поништава се само фунцтион1_виртуал.
Отуда видимо да у изведеној класи втабле, показивач функције за фунцтион1_виртуал указује на надјачану функцију у изведеној класи. С друге стране, показивач функције за фунцтион2_виртуал указује на функцију у основној класи.
Тако у горњем програму када је основном показивачу додијељен изведени објект класе, основни показивач показује на _вптр изведене класе.
Дакле, када се изврши позив б-> фунцтион1_виртуал (), позива се фунцтион1_виртуал из изведене класе и када се изврши позив функције б-> фунцтион2_виртуал (), пошто овај показивач функције показује на функцију основне класе, функцију основне класе се зове.
Чисте виртуелне функције и апстрактни час
Детаље о виртуелним функцијама на језику Ц ++ видели смо у претходном одељку. У Ц ++ такође можемо дефинисати „ чиста виртуелна функција ”Који се обично изједначава са нулом.
Чиста виртуелна функција је декларисана као што је приказано у наставку.
virtual return_type function_name(arg list) = 0;
Класа која има најмање једну чисту виртуелну функцију која се назива „ апстрактни час ”. Никада не можемо инстанцирати апстрактну класу, тј. Не можемо створити објекат апстрактне класе.
То је зато што знамо да се унос врши за сваку виртуелну функцију у ВТАБЛЕ (виртуелна табела). Али у случају чисте виртуелне функције, овај унос је без адресе, што га чини непотпуним. Дакле, компајлер не дозвољава стварање објекта за класу са непотпуним уносом ВТАБЛЕ.
То је разлог због којег не можемо да направимо инстанцу апстрактне класе.
Следећи пример ће показати чисту виртуелну функцију као и класу Сажетак.
#include using namespace std; class Base_abstract { public: virtual void print() = 0; // Pure Virtual Function }; class Derived_class:public Base_abstract { public: void print() { cout <<'Overriding pure virtual function in derived class
'; } }; int main() { // Base obj; //Compile Time Error Base_abstract *b; Derived_class d; b = &d; b->print(); }
Излаз:
Замена чисте виртуелне функције у изведеној класи
У горњем програму имамо класу дефинисану као Басе_абстрацт која садржи чисту виртуелну функцију што је чини апстрактном класом. Затим изведемо класу „Изведена_класа“ из Басе_абстрацт и заменимо чист испис виртуелне функције у њој.
У главној функцији се не коментарише тај први ред. То је зато што ако га коментаришемо, компајлер ће дати грешку јер не можемо створити објекат за апстрактну класу.
Али други ред даље код ради. Можемо успешно створити показивач основне класе и тада му доделимо изведени објекат класе. Даље, позивамо функцију исписа која даје садржај функције исписа замењене у изведеној класи.
Наведимо укратко неке карактеристике апстрактне наставе:
- Не можемо да направимо инстанцу апстрактне класе.
- Апстрактна класа садржи најмање једну чисту виртуелну функцију.
- Иако не можемо инстанцирати апстрактну класу, увек можемо створити показиваче или референце на ову класу.
- Апстрактна класа може имати неке имплементације попут својстава и метода, заједно са чистим виртуелним функцијама.
- Када изведемо класу из апстрактне класе, изведена класа треба да надјача све чисте виртуелне функције у апстрактној класи. Ако то није успело, изведена класа ће такође бити апстрактна класа.
Виртуелни деструктори
Деструктори класе могу се прогласити виртуелним. Кад год извршимо упцаст, тј. Додељивање изведених објеката класе показивачу основне класе, обични деструктори могу произвести неприхватљиве резултате.
На пример,размотрите следеће надоградње уобичајеног деструктора.
#include using namespace std; class Base { public: ~Base() { cout << 'Base Class:: Destructor
'; } }; class Derived:public Base { public: ~Derived() { cout<< 'Derived class:: Destructor
'; } }; int main() { Base* b = new Derived; // Upcasting delete b; }
Излаз:
Основна класа :: Деструктор
У горњем програму имамо наслеђену изведену класу из основне класе. У основи, додељујемо објекат изведене класе показивачу основне класе.
У идеалном случају, деструктор који се позива када се зове „делете б“ требао је бити изведене класе, али из резултата можемо видети да се деструктор основне класе назива док показивач основне класе указује на то.
Због тога се изведени деструктор класе не позива и изведени објект класе остаје нетакнут, што резултира цурењем меморије. Решење за то је конструктор основне класе учинити виртуелним тако да показивач објекта показује исправни деструктор и врши се правилно уништавање објеката.
Употреба виртуелног деструктора приказана је у доњем примеру.
#include using namespace std; class Base { public: virtual ~Base() { cout << 'Base Class:: Destructor
'; } }; class Derived:public Base { public: ~Derived() { cout<< 'Derived class:: Destructor
'; } }; int main() { Base* b = new Derived; // Upcasting delete b; }
Излаз:
Изведена класа :: Деструцтор
Основна класа :: Деструктор
Ово је исти програм као и претходни програм, осим што смо додали виртуелну кључну реч испред деструктора основне класе. Чинећи деструктор основне класе виртуелним, постигли смо жељени излаз.
Можемо видети да када показивачу основне класе доделимо изведени објект класе, а затим избришемо показивач основне класе, деструктори се позивају обрнутим редоследом креирања објекта. То значи да се прво позива изведени деструктор класе, а објект се уништава, а затим се уништава објект основне класе.
Белешка: У Ц ++, конструктори никада не могу бити виртуелни, јер су конструктори укључени у конструкцију и иницијализацију објеката. Отуда нам требају сви конструктори да се изврше у потпуности.
Закључак
Рунтиме полиморфизам је имплементиран применом методе која замењује. Ово добро функционише када методе позивамо са одговарајућим објектима. Али када имамо показивач основне класе и позивамо надјачане методе користећи показивач основне класе који показује на изведене објекте класе, долази до неочекиваних резултата због статичког повезивања.
Да бисмо то превазишли, користимо концепт виртуелних функција. Помоћу интерног представљања втаблес и _вптр, виртуелне функције помажу нам да тачно позовемо жељене функције. У овом упутству смо детаљно видели полиморфизам времена извођења који се користи у Ц ++.
јава додај на крај низа
Овим закључујемо наше водиче о објектно оријентисаном програмирању на језику Ц ++. Надамо се да ће овај водич бити користан за боље и темељније разумевање објектно оријентисаних концепата програмирања на језику Ц ++.
=> Посетите овде да бисте научили Ц ++ из огреботина.
Препоручено читање
- Полиморфизам у Ц ++
- Наслеђивање у Ц ++
- Функције пријатеља у Ц ++
- Класе и објекти на језику Ц ++
- Коришћење класе Селениум Селецт за руковање падајућим елементима на веб страници - Водич за селениј бр. 13
- Водич за главне функције Питхона са практичним примерима
- Јава виртуелна машина: како ЈВМ помаже у покретању Јава апликације
- Како поставити ЛоадРуннер ВуГен Сцрипт датотеке и поставке рунтиме-а