C++ virtual funksiyalari
Virtual funksiya bu class bazasidagi function aʼzosi boʻlib, biz hosil boʻlgan sinflarda qayta belgilashni kutmoqdamiz.
Masalan,
class Base {
public:
void print() {
// kod
}
};
class Derived : public Base {
public:
void print() {
// kod
}
};
shadows Derived sinfidagi print() usuli Base sinfidagi print() usuli.
Biroq, agar biz Derived sinfidagi ob'ektga ishora qilish uchun Base tipidagi ko'rsatkich yaratsak va print() funksiyasini chaqirsak, u Base sinfining print() funksiyasini chaqiradi.
Boshqacha qilib aytganda, Base a'zo funksiyasi bekor qilinmaydi.
int main() {
Derived derived1;
Base* base1 = &derived1;
// Base sinfining chaqiruv funksiyasi
base1->print();
return 0;
}
Bunga yo'l qo'ymaslik uchun virtual kalit so'zidan foydalanib, Base sinfining print() funksiyasini virtual deb e'lon qilamiz.
class Base {
public:
virtual void print() override {
// kod
}
};
Agar virtual funksiya olingan sinfda qayta aniqlangan bo'lsa, olingan sinfdagi funksiya, olingan sinf ob'ektiga ishora qiluvchi asosiy sinf ob'ektining pointer yordamida chaqirilgan taqdirda ham bajariladi. Bunday holda, funksiya bekor qilingan deb aytiladi.
Virtual funksiyalar C++ tilidagi polimorfizmning ajralmas qismidir. Qo'shimcha ma'lumot olish uchun C++ Polymorphism bo'yicha o'quv qo'llanmamizni tekshiring.
#include <iostream>
using namespace std;
class Base {
public:
virtual void print() {
cout << "Base Function" << endl;
}
};
class Derived : public Base {
public:
void print() override {
cout << "Derived Function" << endl;
}
};
int main() {
Derived derived1;
// olingan1 ga ishora qiluvchi Baza tipidagi ko'rsatkich
Base* base1 = &derived1;
// Derived sinfining a'zo funksiyasini chaqiradi
base1->print();
return 0;
}
Chiqish
Derived Function
Bu erda biz Base ning print() usulini virtual deb e'lon qildik.
Shunday qilib, biz Derived obyektiga ishora qiluvchi Base tipidagi ko'rsatgichdan foydalansak ham, bu funksiya bekor qilinadi1.
Working of virtual functions in C++

C++ 11 yangi override spetsifikatsiyasini taqdim etadi, bu virtual funksiyalardan foydalanishda keng tarqalgan xatolardan qochish uchun juda foydali.
Ushbu override spetsifikatsiyasi, asosiy sinfning a'zo funksiyasini bekor qiladigan hosila sinflarning a'zo funksiyalarini belgilaydi.
Masalan,
class Base {
public:
virtual void print() {
// kod
}
};
class Derived : public Base {
public:
void print() override {
// kod
}
};
Agar biz Derived sinfida funksiya prototipidan foydalansak va bu funksiyani sinfdan tashqarida aniqlasak, biz quyidagi koddan foydalanamiz:
class Derived : public Base {
public:
// funksiya prototipi
void print() override;
};
// funksiya ta'rifi
void Derived::print() {
// kod
}
Bu erda void print() override; Derived sinfidagi funksiya prototipidir. override specifier Base sinfidagi print() funksiyasi Derived sinfidagi print() funksiyasi tomonidan bekor qilinishini ta'minlaydi.
Agar biz Derived sinfida funksiya prototipidan foydalansak, u holda override spetsifikatsiyasidan ta'rifda emas, faqat funksiya deklaratsiyasida foydalanamiz.
Virtual funksiyalardan foydalanganda hosila sinflarning a'zo funksiyalarini e'lon qilishda xatolikka yo'l qo'yish mumkin.
override spetsifikatsiyasidan foydalanish kompilyatorni ushbu xatolar sodir bo'lganda xato xabarlarini ko'rsatishni taklif qiladi.
Aks holda, dastur shunchaki kompilyatsiya qilinadi, lekin virtual funksiya bekor qilinmaydi.
Ushbu mumkin bo'lgan xatolardan ba'zilari:
print() deb nomlangan bo'lsa, lekin biz tasodifan olingan sinfdagi bekor qiluvchi funksiyani pint() deb nomlaymiz.void tipidagi bo‘lsa, lekin olingan sinfdagi funksiya int tipidagi bo‘lsa.Agar olingan sinf dinamik xotira ajratishni o'z ichiga olgan bo'lsa, biz uning destruktoridagi xotirani ajratishimiz kerak.
#include <iostream>
using namespace std;
class Shape {
public:
Shape() = default;
virtual double get_area() const {
return 0.0;
}
~Shape() = default;
};
class Square: public Shape {
private:
double* length;
public:
Square(double len = 1.0): length(new double{len}) {
}
double get_area() const override {
return *length * *length;
}
~Square() {
delete length;
cout << "deleted length." << endl << endl;
}
};
int main() {
// Square ob'ektiga ishora qiluvchi Square sinfiga ko'rsatkich
Square* shape1 = new Square(1.5);
cout << "area of square: " << shape1->get_area() << endl;
// Square sinf destruktorini chaqiradi
delete shape1;
// Square ob'ektiga ishora qiluvchi Shape sinfiga ko'rsatkich
Shape* shape2 = new Square(2.5);
cout << "area of square: " << shape2->get_area() << endl;
// Shakl sinfi destruktorini chaqiradi
delete shape2;
return 0;
}
Chiqish:
area of square: 2.25
deleted length.
area of square: 6.25
Kvadrat ob'ektga ishora qiluvchi Square* bo'lgan shape1 ni o'chirsak, Square klassining destruktori chaqiriladi, bu Square ob'ekti tomonidan olingan dinamik xotirani o'chiradi.
Biroq, agar biz Square ob'ektiga ishora qiluvchi Shape* bo'lgan shape2 ni o'chirsak, Shape sinfining destruktori chaqiriladi, bu Square ob'ekti tomonidan olingan dinamik xotirani ajratmaydi.
Agar Shape sinf destruktorini virtual deb e'lon qilsak, Square sinf destruktori Square ob'ektiga ishora qiluvchi Shape sinfiga ko'rsatgichni o'chirganimizda chaqiriladi.
#include <iostream>
using namespace std;
class Shape {
public:
Shape() = default;
virtual double get_area() const {
return 0.0;
}
virtual ~Shape() = default;
};
class Square: public Shape {
private:
double* length;
public:
Square(double len = 1.0): length(new double{len}) {
}
double get_area() const override {
return *length * *length;
}
~Square() {
delete length;
cout << "deleted length." << endl << endl;
}
};
int main() {
// Square ob'ektiga ishora qiluvchi Square sinfiga ko'rsatkich
Square* shape1 = new Square(1.5);
cout << "area of square: " << shape1->get_area() << endl;
// Square sinf destruktorini chaqiradi
delete shape1;
// Square ob'ektiga ishora qiluvchi Shape sinfiga ko'rsatkich
Shape* shape2 = new Square(2.5);
cout << "area of square: " << shape2->get_area() << endl;
// kvadrat sinf destruktorini chaqiradi
delete shape2;
return 0;
}
Chiqish:
area of square: 2.25
deleted length.
area of square: 6.25
deleted length.
Virtual funksiyalar biz bir guruh ob'ektlarni saqlaganimizda foydalidir.
Faraz qilaylik, bizda asosiy toifa Xodim va olingan sinflar HourlyEmployee va RegularEmployee. Biz ikkala hosila sinf ob'ektlariga ishora qiluvchi Xodim* ko'rsatkichlarini Xodim* to'plamida saqlashimiz mumkin.
Keyin biz Employee* ko'rsatkichlari yordamida virtual funksiyani chaqirishimiz mumkin. Biz virtual funksiyani Employee* ko'rsatkichlari yordamida chaqirganimizda, kompilyator tegishli olingan sinfda belgilangan funksiyani chaqiradi.
// Virtual funksiyadan foydalanishni ko'rsatish uchun C++ dasturi
#include <iostream>
#include <vector>
using namespace std;
class Employee {
private:
string first_name;
string last_name;
public:
Employee(string fname, string lname): first_name(fname), last_name(lname) {
}
string get_full_name() {
return first_name + " " + last_name;
}
// bekor qilishga ruxsat berish uchun virtual funksiya
virtual void print_wage() {
cout << "The employee's wage structure is not declared yet" << endl;
}
// virtual destruktor
virtual ~Employee() {
}
};
class HourlyEmployee: public Employee {
private:
double hourly_wage;
public:
HourlyEmployee(string fname, string lname, double wage_per_hour): Employee(fname, lname), hourly_wage(wage_per_hour) {
}
// funksiyani bekor qiladi va to'g'ri bajarilishini ta'minlaydi
// HourlyEmployee ish haqi tarkibiga ko'ra
void print_wage() override {
cout << "The hourly wage of " << get_full_name() << " is " << hourly_wage << endl;
}
};
class RegularEmployee: public Employee {
private:
double monthly_wage;
public:
RegularEmployee(string fname, string lname, double wage_per_month): Employee(fname, lname), monthly_wage(wage_per_month) {
}
// funksiyani bekor qiladi va to'g'ri bajarilishini ta'minlaydi
// Muntazam xodimning ish haqi tarkibiga ko'ra
void print_wage() override {
cout << "The monthly wage of " << get_full_name() << " is " << monthly_wage << endl;
}
};
int main() {
// Dervied sinf ob'ektlariga ishora qiluvchi Employee* ko'rsatkichlarini saqlash uchun vektorni e'lon qiling
vector<Employee*> employees {new HourlyEmployee("John", "Doe", 13.5), new RegularEmployee("Jane", "Smith", 3000.7)};
for(Employee* employee: employees) {
employee->print_wage();
}
return 0;
}
Chiqish
The hourly wage of John Doe is 13.5
The monthly wage of Jane Smith is 3000.7
Bu yerda “Xodim”ning asosiy klassi print_wage() virtual funksiyasiga ega. HourlyEmployee va RegularEmployee hosila sinflari print_wage() funksiyasini o'z amalga oshirishiga ega.
Biz vektorda HourlyEmployee va RegularEmployee obyektlariga ishora qiluvchi Employee* ko‘rsatkichlarini saqladik.
vector<Employee*> employees {new HourlyEmployee("John", "Doe", 13.5), new RegularEmployee("Jane", "Smith", 3000.7)};
Keyin vektordagi ko'rsatkichlar bo'ylab takrorlash uchun intervalli for loop dan foydalandik.
for(Employee* employee: employees) {
employee->print_wage();
}
Bu erda employee->print_wage() print_wage() funksiyasini ko'rsatuvchi xodim ko'rsatadigan ob'ekt turiga qarab chaqiradi.