C++ Полиморфизъм с пример

⚡ Умно обобщение

Полиморфизъм в C++ позволява на една член-функция да приема много форми, като се държи различно в зависимост от обекта, който я извиква. Тя се основава на наследяване и се появява в два варианта, разрешени или по време на компилация, или по време на изпълнение.

  • 🔷 Определение: Полиморфизмът означава „много форми“, позволявайки на една функция член да се държи различно в зависимост от извикващия обект.
  • 🧭 Два вида: C++ предоставя полиморфизъм по време на компилация (статичен) и полиморфизъм по време на изпълнение (динамичен).
  • 🧩 Време за компилиране: Претоварването на функции и претоварването на оператори избира правилната функция по време на компилация.
  • 🔁 Време на изпълнение: Функциите за преодоляване на функции и виртуалните функции разрешават повикването, докато програмата се изпълнява.
  • ️ Виртуални функции: Ключовата дума virtual в базов клас позволява късно свързване чрез указател към базов клас.
  • 🤖 AI помощ: GitHub Copilot и подобни асистенти изграждат претоварени, презаписани и виртуални функции от кратък коментар.

C++ полиморфизъм

Какво представлява полиморфизмът в C++?

In C++, полиморфизмът кара член-функцията да се държи различно в зависимост от обекта, който я извиква. Полиморфизмът е гръцка дума, която означава да имаш много форми. Това се случва, когато имате йерархия от класове свързани чрез наследство.

Да предположим например, че имаме функцията makeSound(). Когато котката извика тази функция, тя ще издаде мяукане. Когато крава извика същата функция, тя ще издаде звука на мучене.

Полиморфизъм в C++ пример за makeSound

Въпреки че имаме една функция, тя се държи различно при различни обстоятелства. Функцията има много форми; следователно сме постигнали полиморфизъм.

С основната идея налице, C++ организира това поведение в две отделни категории.

Видове полиморфизъм

C++ поддържа два вида полиморфизъм:

  • Полиморфизъм по време на компилиране и
  • Полиморфизъм по време на изпълнение.

Видове полиморфизъм в C++

Първата категория разрешава всяко извикване на функция, преди програмата да се изпълни.

Полиморфизъм на времето за компилиране

Вие извиквате претоварените функции чрез съпоставяне на броя и типа на аргументите. Информацията присъства по време на компилиране. Това означава, че C++ компилаторът ще избере правилната функция по време на компилиране.

Полиморфизмът по време на компилиране се постига чрез претоварване на функции и оператори.

Претоварване на функцията

Претоварването на функциите възниква, когато имаме много функции с подобни имена, но различни аргументи. Аргументите може да се различават по брой или тип.

Пример 1

#include <iostream> 
using namespace std;

void test(int i) {
	cout << " The int is " << i << endl;
}
void test(double  f) {
	cout << " The float is " << f << endl;
}
void test(char const *ch) {
	cout << " The char* is " << ch << endl;
}

int main() {
	test(5);
	test(5.5);
	test("five");
	return 0;
}

Изход:

C++ претоварване на функция Пример 1 изход

Ето екранна снимка на кода:

C++ претоварване на функции Пример 1 код

Code Обяснение:

  1. Включете заглавния файл на iostream в нашия код. Ще можем да използваме неговите функции.
  2. Включете пространството от имена std в нашия код. Ще можем да използваме неговите класове, без да го извикваме.
  3. Създайте функция с име test, която приема целочислен параметър i. { маркира началото на тялото на функционалния тест.
  4. Инструкция, която трябва да бъде изпълнена, ако горният функционален тест бъде извикан/извикан.
  5. Край на тялото на горния функционален тест.
  6. Създайте функция с име test, която приема плаващ параметър f. { маркира началото на тялото на функционалния тест.
  7. Инструкция, която трябва да бъде изпълнена, ако горният функционален тест бъде извикан/извикан.
  8. Край на тялото на горния функционален тест.
  9. Създайте функция с име test, която приема символен параметър ch. { маркира началото на тялото на функционалния тест.
  10. Инструкция, която трябва да бъде изпълнена, ако горният функционален тест бъде извикан/извикан.
  11. Край на тялото на горния функционален тест.
  12. Извикайте функцията main(). { отбелязва началото на тялото на функцията.
  13. Извикайте теста на функцията и му предайте 5 като стойност на аргумента. Това извиква тестовата функция, която приема целочислен аргумент, т.е. първата тестова функция.
  14. Извикайте теста на функцията и му предайте 5.5 като стойност на аргумента. Това ще извика тестовата функция, която приема плаващ аргумент, тоест втората тестова функция.
  15. Извикайте теста на функцията и му подайте пет като стойност на аргумента. Това ще извика тестовата функция, която приема символен аргумент, т.е. третата тестова функция.
  16. Програмата трябва да върне стойност, ако работи успешно.
  17. Краят на тялото на функцията main().

Имаме три функции с едно и също име, но различни типове аргументи. Постигнахме полиморфизъм.

Operator Претоварване

In Operator Претоварване, ние дефинираме ново значение за a C++ операторТова променя и начина, по който работи операторът. Например, можем да дефинираме оператора + за конкатенация на два низа. Познаваме го като оператор за събиране за събиране на числови стойности. След нашата дефиниция, когато е поставен между цели числа, той ще ги събере. Когато е поставен между низове, той ще ги конкатенира.

Пример 2

#include<iostream> 
using namespace std;

class ComplexNum {
private:
	int real, over;
public:
	ComplexNum(int rl = 0, int ov = 0) {
		real = rl;   
		over = ov; 
	}

	ComplexNum operator + (ComplexNum const &obj) {
		ComplexNum result;
		result.real = real + obj.real;
		result.over = over + obj.over;
		return result;
	}
	void print() { 
		cout << real << " + i" << over << endl; 
	}
};
int main()
{
	ComplexNum c1(10, 2), c2(3, 7);
	ComplexNum c3 = c1+c2;
	c3.print();
}

Изход:

C++ претоварване на оператора Пример 2 изход

Ето екранна снимка на кода:

C++ Претоварване на оператори Пример 2 код

C++ Резултат от претоварване на оператори от Пример 2

Code Обяснение:

  1. Включете заглавния файл на iostream в нашата програма, за да използвате неговите функции.
  2. Включете пространството от имена std в нашата програма, за да използвате неговите класове, без да го извиквате.
  3. Създайте клас с име ComplexNum. { отбелязва началото на тялото на класа.
  4. Използвайте модификатора за частен достъп, за да маркирате променливите като частни, което означава, че те могат да бъдат достъпни само от класа.
  5. Дефинирайте две целочислени променливи, real и over.
  6. Използвайте модификатора за достъп public, за да маркирате конструктора като public, което означава, че той ще бъде достъпен дори извън класа.
  7. Създайте конструктора на класа и инициализирайте променливите.
  8. Инициализирайте стойността на променливата real.
  9. Инициализирайте стойността на променливата над.
  10. Край на тялото на конструктора.
  11. Трябва да заменим значението на оператора +.
  12. Създайте резултат от тип данни от тип ComplexNum.
  13. Използвайте оператора + с комплексни числа. Този ред ще добави реалната част на число към реалната част на друго число.
  14. Използвайте оператора + с комплексни числа. Този ред ще добави имагинерната част на число към имагинерната част на друго число.
  15. Програмата ще върне стойността на променливата резултат при успешно изпълнение.
  16. Край на дефиницията на новото значение на оператора +, тоест претоварване.
  17. Извикайте метода print().
  18. Отпечатайте новото комплексно число след добавяне на конзолата.
  19. Край на тялото на функцията print().
  20. Край на тялото на клас ComplexNum.
  21. Извикайте функцията main().
  22. Предайте стойностите както на реалните, така и на комплексните части, които да бъдат добавени. Първата част на c1 ще бъде добавена към първата част на c2, тоест 10+3. Втората част на c1 ще бъде добавена към втората част на c, тоест 2+7.
  23. Извършете операция, като използвате претоварения оператор + и запишете резултата в променлива c3.
  24. Отпечатайте стойността на променлива c3 на конзолата.
  25. Край на тялото на функцията main().

Докато полиморфизмът по време на компилация взема решение рано, втората категория отлага избора до изпълнението.

Полиморфизъм по време на изпълнение

Това се случва, когато методът на обект се извиква/извиква по време на изпълнение, а не по време на компилиране. Полиморфизмът по време на изпълнение се постига чрез замяна на функцията. Функцията, която трябва да бъде извикана/извикана, се установява по време на изпълнение.

Замяна на функцията

Отмяната на функция се случва, когато на функция от базовия клас се даде нова дефиниция в производен клас. По това време можем да кажем, че основната функция е била отменена.

Например:

#include <iostream>
using namespace std;
class Mammal {

public:
	void eat() {

		cout << "Mammals eat...";
	}

};

class Cow: public Mammal {

public:
	void eat() {

		cout << "Cows eat grass...";
	}
};
int main(void) {

	Cow c = Cow();

	c.eat();

	return 0;

}

Изход:

C++ функция, отменяща изхода

Ето екранна снимка на кода:

C++ код за заместване на функции

Code Обяснение:

  1. Импортирайте заглавния файл на iostream в нашата програма, за да използвате неговите функции.
  2. Включете пространството от имена std в нашата програма, за да използвате неговите класове, без да го извиквате.
  3. Създайте клас с име Mammal. { отбелязва началото на тялото на класа.
  4. Използвайте модификатора за публичен достъп, за да зададете функцията, която ще създадем, като публично достъпна. Ще бъде достъпен извън този клас.
  5. Създайте публична функция, наречена яде. { отбелязва началото на тялото на функцията.
  6. Отпечатайте оператора, добавен към функцията cout, когато функцията eat() е извикана.
  7. Краят на тялото на функцията eat().
  8. Край на тялото на клас Бозайник.
  9. Създайте клас с име Крава, който наследява класа Бозайник. Кравата е производният клас, докато бозайникът е базовият клас. { отбелязва началото на този клас.
  10. Използвайте модификатора за публичен достъп, за да маркирате функцията, която ще създадем, като публично достъпна. Ще бъде достъпен извън този клас.
  11. Заменете функцията eat(), която е дефинирана в базовия клас. { отбелязва началото на тялото на функцията.
  12. Инструкцията за отпечатване на конзолата, когато се извика тази функция.
  13. Край на тялото на функцията eat().
  14. Край на тялото на класа Крава.
  15. Извикайте функцията main(). { маркира началото на тялото на тази функция.
  16. Създайте екземпляр на класа Cow и му дайте име c.
  17. Извикайте функцията eat(), дефинирана в класа Cow.
  18. Програмата трябва да върне стойност при успешно завършване.
  19. Край на функцията main().

C++ Виртуална функция

Виртуалната функция е друг начин за внедряване на полиморфизъм по време на изпълнение в C++. Това е специална функция, дефинирана в базов клас и предефинирана в производния клас. За да декларирате виртуална функция, трябва да използвате ключовата дума virtual. Ключовата дума трябва да предхожда декларацията на функцията в базовия клас.

Ако клас на виртуална функция е наследен, виртуалният клас предефинира виртуалната функция, за да отговаря на нуждите си. Например:

#include <iostream>  
using namespace std;
class ClassA {
		public:
		virtual void show() {
			cout << "The show() function in base class invoked..." << endl;
		}
	};
	class ClassB :public ClassA {
	public:
		void show() 	{
			cout << "The show() function in derived class invoked...";
		}
	};
	int main() {
		ClassA* a;   
		ClassB b;
		a = &b;
		a->show();      
	}

Изход:

C++ изход на виртуална функция

Ето екранна снимка на кода:

C++ код на виртуална функция

Code Обяснение:

  1. Включете заглавния файл на iostream в кода, за да използвате неговите функции.
  2. Включете пространството от имена std в нашия код, за да използвате неговите класове, без да го извиквате.
  3. Създайте клас с име ClassA.
  4. Използвайте модификатора за публичен достъп, за да маркирате член на класа като публично достъпен.
  5. Създайте виртуална функция с име show(). Това ще бъде публична функция.
  6. Текстът за отпечатване, когато се извика извиканият show(). Краят е a C++ ключова дума, което означава краен ред. Той премества курсора на мишката на следващия ред.
  7. Край на тялото на виртуалната функция show().
  8. Край на тялото на класа ClassA.
  9. Създаване на нов клас с име ClassB, който наследява класа ClassA. ClassA става базов клас, докато ClassB става производен клас.
  10. Използвайте модификатора за публичен достъп, за да маркирате член на класа като публично достъпен.
  11. Предефинирайте виртуалната функция show(), получена в базовия клас.
  12. Текстът за отпечатване на конзолата, когато се извика функцията show(), дефинирана в производния клас.
  13. Край на тялото на функцията show().
  14. Край на тялото на производния клас, ClassB.
  15. Извикайте функцията main(). Логиката на програмата трябва да бъде добавена в нейното тяло.
  16. Създайте променлива указател с име a. Той сочи към класа с име ClassA.
  17. Създайте екземпляр на класа с име ClassB. Екземплярът получава името b.
  18. Присвоете стойностите, съхранявани в адреса b в променливата a.
  19. Извикване на функцията show(), дефинирана в производния клас. Приложено е късно свързване.
  20. Край на тялото на функцията main().

След като и двата механизма са обхванати, таблицата по-долу ги сравнява един до друг.

Полиморфизъм по време на компилиране Vs. Полиморфизъм по време на изпълнение

Ето основните разлики между двете:

Полиморфизъм по време на компилация Полиморфизъм по време на изпълнение
Нарича се още ранно свързване или статичен полиморфизъм Нарича се още късно/динамично свързване или динамичен полиморфизъм
Методът се извиква/извиква по време на компилиране Методът се извиква/извиква по време на изпълнение
Реализирано чрез претоварване на функции и претоварване на оператори Внедрено чрез заместване на метод и виртуални функции
Пример, претоварване на метода. Много методи могат да имат подобни имена, но различен брой или типове аргументи Пример, отмяна на метод. Много методи могат да имат подобно име и същия прототип.
По-бързо изпълнение, тъй като откриването на метода се извършва по време на компилация По-бавно изпълнение, тъй като откриването на методите се извършва по време на изпълнение.
Less осигурена е гъвкавост за решаване на проблеми, тъй като всичко е известно по време на компилиране. Осигурена е голяма гъвкавост за решаване на сложни проблеми, тъй като методите се откриват по време на изпълнение.

Въпроси и Отговори

Претоварването дефинира няколко функции с едно и също име, но с различни параметри, разрешавани по време на компилация без наследяване. Пренареждането предефинира наследена функция от базов клас със същата сигнатура в производен клас, разрешавана по време на изпълнение, когато функцията е декларирана като виртуална.

Чиста виртуална функция се декларира с = 0 и няма тяло в базовия клас. Всеки клас, съдържащ такова, става abstract клас, който не може да бъде създаден директно и принуждава производните класове да предоставят своя собствена имплементация.

Когато даден клас декларира виртуални функции, компилаторът изгражда виртуална таблица (vtable) от указатели към функции. Всеки обект съхранява скрит указател към своя клас vtable, така че указателят към базовия клас извиква правилната презаписана функция по време на изпълнение чрез късно обвързване.

Да. Шаблоните позволяват на една функция или клас да работи с много типове данни, като компилаторът генерира специфичен за типа код по време на компилацията. Това обобщено програмиране е форма на статичен полиморфизъм, която допълва претоварването на функции и претоварването на оператори.

Когато изтриете производен обект чрез указател към базов клас, само виртуален деструктор гарантира, че производният деструктор също ще се изпълни. Без него почистването е непълно и причинява изтичане на памет. Декларирането на базовия деструктор като виртуален кара унищожаването да се държи полиморфно.

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

Да. Асистентите за кодиране с изкуствен интелект превръщат кратки коментари в претоварени функции, виртуални базови класове и производни презаписвания. Те бързо обработват повтарящи се шаблони, въпреки че все пак трябва да проверите виртуалните ключови думи, сигнатурите на функциите и безопасността на деструктора, преди да компилирате генерирания код.

Да. Копилот на GitHub Предлага декларации на виртуални функции, замествания на производни класове и претоварвания на оператори, докато пишете. Ускорява шаблонната версия за йерархии на наследяване, въпреки че трябва да потвърдите ключовата дума virtual, съвпадащите сигнатури и да коригирате поведението при късно свързване преди компилиране.

Обобщете тази публикация с: