14-ma’ruza. Sinfning virtual metodlari va ularni ishlatish mexanizmlari. Abstrakt sinflar, ularning mo’ljallanishi va xususiyatlari



Yüklə 31,62 Kb.
səhifə1/6
tarix02.12.2022
ölçüsü31,62 Kb.
#71888
  1   2   3   4   5   6
14 ma’ruza Sinfning virtual metodlari va ularni ishlatish mexanizmlari


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:


#include


class Parent
{
public:
virtual const char* getName() { return "Parent"; } // добавили ключевое слово virtual
};
class Child: public Parent
{
public:
virtual const char* getName() { return "Child"; }
};
int main()
{
Child child;
Parent &rParent = child;
std::cout << "rParent: " << rParent.getName() << '\n';
return 0;
}

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"; }
};

class B: public A


{
public:
virtual const char* getName() { return "B"; }
};

class C: public B


{
public:
virtual const char* getName() { return "C"; }
};

class D: public C


{
public:
virtual const char* getName() { return "D"; }
};

int main()


{
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;
}

public:
string getName() { return m_name; }


const char* speak() { return " ???"; }
};

class Cat: public Animal


{
public:
Cat(string name)
: Animal(name)
{
}

const char* speak() { return "Meow"; }


};

class Dog: public Animal


{
public:
Dog(string name)
: Animal(name)
{
}

const char* speak() { return "Woof"; }


};

void report(Animal &animal)


{
cout << animal.getName() << animal.speak() <<" deydi"<< '\n';
}

int main()


{
Cat cat("Matros");
Dog dog("Barsik");

report(cat);


report(dog);
}

Dasturni bajarish natijasi:


Matros ??? deydi


Barsik ??? deydi

Keling, xuddi shu sinfni speak () metodini virtual qilish orqali ko'rib chiqaylik:


#include


#include
using namespace std;
class Animal
{
protected:
string m_name;
Animal(string name)
{
m_name = name;
}

public:
string getName() { return m_name; }


virtual const char* speak() { return "???"; }
};

class Cat: public Animal


{
public:
Cat(string name)
: Animal(name)
{
}

virtual const char* speak() { return "Meow"; }


};

class Dog: public Animal


{
public:
Dog(string name)
: Animal(name)
{
}

virtual const char* speak() { return "Woof"; }


};

void report(Animal &animal)


{
cout << animal.getName() << " " << animal.speak() <<" deydi"<< '\n';
}

int main()


{
Cat cat("Matros");
Dog dog("Barsik");

report(cat);


report(dog);
}

Dasturni bajarish natijasi:


Matros Miyav deydi


Barsik Vuf deydi

Bu ishladi!


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");

Animal *animals[] = { &matros, &barsik, &ivan, &tolik, &martun, &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.

Yüklə 31,62 Kb.

Dostları ilə paylaş:
  1   2   3   4   5   6




Verilənlər bazası müəlliflik hüququ ilə müdafiə olunur ©azkurs.org 2024
rəhbərliyinə müraciət

gir | qeydiyyatdan keç
    Ana səhifə


yükləyin