14-ma’ruza. Sinfning virtual metodlari va ularni ishlatish mexanizmlari. Abstrakt sinflar, ularning mo’ljallanishi va xususiyatlari.
Oldingi darsda biz bir qator misollarni ko'rib chiqdik, ularda ajdod sinfning ko'rsatkichlari yoki havolalaridan foydalanish mantiqni soddalashtirdi va kod miqdorini kamaytirdi. Virtual funksiyalar va polimorfizm.Biroq, biz muammoga duch keldik, bunda ajdod ko'rsatkichi yoki havolasi faqat ajdod metodlarini chaqiradi, avlodni emas. Masalan:
#include class Parent
{
public:
const char* getName() { return "Parent"; }
};
class Child: public Parent
{
public:
const char* getName() { return "Child"; }
};
int main()
{
Child child;
Parent &rParent = child;
std::cout << "rParent: " << rParent.getName() << '\n';
}
rParent Parent sinfiga havola bo'lgani uchun Parent::getName() chaqiriladi, garchi biz aslida child obyektning Parent sinfiga havola ham.
Quyida biz virtual funktsiyalardan foydalangan holda ushbu muammoni qanday hal qilishni ko'rib chiqamiz.
C++ tilidagi virtual funksiya funksiyaning maxsus turi bo‘lib, chaqirilganda ajdod va bola sinflari o‘rtasida mavjud bo‘lgan “eng katta” avlod metodini bajaradi. Bu xususiyat polimorfizm deb ham ataladi. Signatura (nom, parametr turlari va usul doimiymi) va bola usulining qaytish turi ajdod-sinfning metodining imzosi va qaytish turiga mos kelganda, avlod metodi chaqiriladi. Bu metodlarni qayta aniqlash deyiladi (yoki "o'zgartirilgan usullar").
Funksiyani virtual qilish uchun funksiya e’lonidan oldin virtual kalit so'zni ko'rsatish kifoya. Masalan:
rParent avlod obyektining ajdodiga havola bo'lganligi sababli, odatda rParent.getName () ni qayta ishlashda Parent :: getName () chaqiriladi. Biroq, Parent :: getName () virtual funktsiya bo'lganligi sababli, kompilyator avlod sinflarida ushbu metodning qayta aniqlanishini ko’radi va kompilyator Child :: getName () ni topadi!
Keling, murakkabroq misolni ko'rib chiqaylik:
#include using namespace std;
class A
{
public:
virtual const char* getName() { return "A"; }
};
{
C c;
A &rParent = c;
cout << "rParent: " << rParent.getName() << '\n';
return 0;
}
Sizningcha, bu dasturning natijasi qanday?
Keling, hamma narsani tartibda ko'rib chiqaylik:
Birinchidan, C sinfining c obyekti yaratiladi.
rParent A sinfining havolasi bo'lib, biz uni c obyektning A qismiga murojaat qilish uchun belgilaymiz.
Keyin rParent.getName () metodi chaqiriladi.
rParent.GetName () ga murojaat qilish A::getName() ga murojaat qilishga olib keladi. Biroq, A::getName() virtual funksiya bo'lganligi sababli, kompilyator A va C o'rtasida "eng katta" avlodning metodini qidiradi. Bu holda, u C::getName ().
E'tibor bering, kompilyator D::getName() ga murojaat qilmaydi, chunki bizning asl ob'ektimiz D sinfi emas, C sinfi edi, shuning uchun faqat A va C sinflari orasidagi metodlar hisobga olinadi.
Dasturni bajarish natijasi:
rParent - bu C
Misol. Yanada murakkabroq misolni ko’ramiz. Oldingi qo'llanmadagi Animal sinfini ko'rib chiqamiz va ba'zi test kodlarini qo’shamiz:
#include
#include using namespace std;
class Animal
{
protected:
string m_name;
Animal(string name)
{
m_name = name;
}
Animal.speak() ni qayta ishlashda kompilyator Animal::speak() virtual funksiya ekanligini ko'radi. Animal Cat obyektining Animal qismiga murojaat qilganda, kompilyator eng katta avlod speak() metodini topish uchun Animal va Cat o'rtasidagi barcha sinflardan o'tadi va u Cat::speak() ni topadi. Animal Dog obyektining Animal qismiga murojaat qilganda, kompilyator Dog::speak() ni topadi.
E'tibor bering, biz Animal::getName() ni virtual funksiyaga aylantirmaganmiz. Buning sababi, getName () hech qachon avlod sinflarida qayta aniqlanmaydi, shuning uchun bu shart emas.
Xuddi shunday hayvonlar massivi bilan quyidagi misolda:
int main()
{
Cat matros("Matros"), ivan("Ivan"), martun("Martun");
Dog barsik("Barsik"), tolik("Tolik"), tyzik("Tyzik");
for (int iii=0; iii < 6; ++iii)
std::cout << animals[iii]->getName() << " says " << animals[iii]->speak() << '\n';
}
Natija:
Matros Miyav deydi
Barsik Vuf deydi
Ivan Miyav deydi
“Vuf” deydi Tolik
Martun Miyav deydi
Tyzik, deydi Vuf
Garchi bu ikkita misolda faqat Cat va Dog sinflari ishlatilsa ham, boshqa avlod sinflari ham bizning report () funksiyamiz va hayvonlar massivi bilan qo'shimcha o'zgartirishlarsiz ishlaydi! Bu, ehtimol, virtual funktsiyalarning eng katta afzalligi - kodingizni dasturchi tomonidan o'zgartirishlar talab qilmasdan, yangi avlod sinflari avtomatik ravishda eski kod bilan ishlaydigan tarzda tuzilish qobiliyatidir!
Ogohlantirish: Avlod sinfining virtual metodi sarlavhasi ajdod-sinfning virtual metodi sarlavhasiga to'liq mos kelishi kerak. Agar avlod metodi ajdoddan boshqa turdagi parametrlarga ega bo'lsa, bu metod chaqirilmaydi.