C++ ความแตกต่างพร้อมตัวอย่าง

Polymorphism คืออะไร C++?

In C++โพลีมอร์ฟิซึมทำให้ฟังก์ชันสมาชิกมีพฤติกรรมแตกต่างกันไปขึ้นอยู่กับอ็อบเจ็กต์ที่เรียกใช้งาน โพลีมอร์ฟิซึมเป็นคำภาษากรีกที่แปลว่ามีหลายรูปแบบ เกิดขึ้นเมื่อคุณมีลำดับชั้นของคลาสที่เกี่ยวข้องกันผ่านการสืบทอด

ตัวอย่างเช่น สมมติว่าเรามีฟังก์ชัน makeSound() เมื่อแมวเรียกใช้ฟังก์ชันนี้ มันจะส่งเสียงร้องเหมียว เมื่อวัวเรียกใช้ฟังก์ชันเดียวกัน มันจะส่งเสียงวัว

ความแตกต่างใน 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;
}

Output:

ฟังก์ชั่นโอเวอร์โหลด

นี่คือภาพหน้าจอของรหัส:

ฟังก์ชั่นโอเวอร์โหลด

คำอธิบายรหัส:

  1. รวมไฟล์ส่วนหัว iostream ลงในโค้ดของเรา เราจะสามารถใช้ฟังก์ชั่นของมันได้
  2. รวมเนมสเปซมาตรฐานไว้ในโค้ดของเรา เราจะสามารถใช้คลาสของมันได้โดยไม่ต้องเรียกมัน
  3. สร้างฟังก์ชันชื่อ test ที่รับพารามิเตอร์จำนวนเต็ม i { เป็นจุดเริ่มต้นของการทดสอบเนื้อความของฟังก์ชัน
  4. คำสั่งที่จะดำเนินการหากมีการเรียกใช้/เรียกใช้การทดสอบฟังก์ชันข้างต้น
  5. ส่วนท้ายของการทดสอบการทำงานข้างต้น
  6. สร้างฟังก์ชันชื่อ test ที่ใช้พารามิเตอร์ float f { เป็นจุดเริ่มต้นของการทดสอบเนื้อความของฟังก์ชัน
  7. คำสั่งที่จะดำเนินการหากมีการเรียกใช้/เรียกใช้การทดสอบฟังก์ชันข้างต้น
  8. ส่วนท้ายของการทดสอบการทำงานข้างต้น
  9. สร้างฟังก์ชันชื่อ test ที่รับพารามิเตอร์อักขระ ch { เป็นจุดเริ่มต้นของการทดสอบเนื้อความของฟังก์ชัน
  10. คำสั่งที่จะดำเนินการหากมีการเรียกใช้/เรียกใช้การทดสอบฟังก์ชันข้างต้น
  11. ส่วนท้ายของการทดสอบการทำงานข้างต้น
  12. เรียกใช้ฟังก์ชัน main() { เป็นจุดเริ่มต้นของเนื้อความของฟังก์ชัน
  13. เรียกใช้การทดสอบฟังก์ชันและส่ง 5 ให้เป็นค่าของอาร์กิวเมนต์ ซึ่งจะเรียกใช้ฟังก์ชันทดสอบที่ยอมรับอาร์กิวเมนต์จำนวนเต็ม ซึ่งก็คือฟังก์ชันทดสอบแรก
  14. เรียกใช้การทดสอบฟังก์ชันและส่ง 5.5 ให้เป็นค่าของอาร์กิวเมนต์ สิ่งนี้จะเรียกใช้ฟังก์ชันทดสอบที่ยอมรับอาร์กิวเมนต์ float นั่นคือฟังก์ชันทดสอบที่สอง
  15. เรียกใช้การทดสอบฟังก์ชันและส่งค่าห้าให้เป็นค่าของอาร์กิวเมนต์ ซึ่งจะเรียกใช้ฟังก์ชันทดสอบที่ยอมรับอาร์กิวเมนต์อักขระ ซึ่งก็คือฟังก์ชันทดสอบตัวที่สาม
  16. โปรแกรมจะต้องส่งคืนค่าหากทำงานได้สำเร็จ
  17. ส่วนท้ายของฟังก์ชัน main()

เรามีฟังก์ชันสามฟังก์ชันที่มีชื่อเดียวกันแต่อาร์กิวเมนต์ต่างกัน เราบรรลุถึงความหลากหลายแล้ว

Operaทอร์โอเวอร์โหลด

In Operator Overloading เราให้คำจำกัดความใหม่แก่ 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();
}

Output:

Operaทอร์โอเวอร์โหลด

นี่คือภาพหน้าจอของรหัส:

Operaทอร์โอเวอร์โหลด

Operaทอร์โอเวอร์โหลด

คำอธิบายรหัส:

  1. รวมไฟล์ส่วนหัว iostream ลงในโปรแกรมของเราเพื่อใช้ฟังก์ชันต่างๆ
  2. รวมเนมสเปซมาตรฐานไว้ในโปรแกรมของเราเพื่อใช้คลาสโดยไม่ต้องเรียกมัน
  3. สร้างคลาสชื่อ ComplexNum เครื่องหมาย { จะเป็นจุดเริ่มต้นของตัวคลาส
  4. ใช้ตัวแก้ไขการเข้าถึงส่วนตัวเพื่อทำเครื่องหมายตัวแปรว่าเป็นส่วนตัว ซึ่งหมายความว่าสามารถเข้าถึงได้จากภายในชั้นเรียนเท่านั้น
  5. กำหนดตัวแปรจำนวนเต็มสองตัว จริงและมากกว่า
  6. ใช้ตัวแก้ไขการเข้าถึงสาธารณะเพื่อทำเครื่องหมายตัวสร้างให้เป็นสาธารณะ ซึ่งหมายความว่าจะสามารถเข้าถึงได้แม้จากภายนอก ชั้น.
  7. สร้างตัวสร้างคลาสและการเริ่มต้นตัวแปร
  8. เริ่มต้นค่าของตัวแปรจริง
  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;

}

Output:

การเอาชนะฟังก์ชัน

นี่คือภาพหน้าจอของรหัส:

การเอาชนะฟังก์ชัน

คำอธิบายรหัส:

  1. นำเข้าไฟล์ส่วนหัว iostream ลงในโปรแกรมของเราเพื่อใช้ฟังก์ชันต่างๆ
  2. รวมเนมสเปซมาตรฐานไว้ในโปรแกรมของเราเพื่อใช้คลาสโดยไม่ต้องเรียกมัน
  3. สร้างคลาสชื่อ Mammal { ถือเป็นจุดเริ่มต้นของเนื้อหาคลาส
  4. ใช้ตัวแก้ไขการเข้าถึงสาธารณะเพื่อตั้งค่าฟังก์ชันที่เรากำลังจะสร้างให้เข้าถึงได้แบบสาธารณะ จะสามารถเข้าถึงได้จากภายนอกชั้นเรียนนี้
  5. สร้างฟังก์ชั่นสาธารณะชื่อกิน { เป็นจุดเริ่มต้นของเนื้อหาฟังก์ชัน
  6. พิมพ์คำสั่งที่เพิ่มเข้าไปในฟังก์ชัน cout เมื่อเรียกใช้ฟังก์ชัน eat()
  7. ส่วนท้ายของฟังก์ชัน eat()
  8. ปิดท้ายด้วยคลาส Mammal
  9. สร้างคลาสชื่อ Cow ที่สืบทอดคลาส Mammal Cow เป็นคลาสที่ได้รับ ในขณะที่ Mammal เป็นคลาสพื้นฐาน { ถือเป็นจุดเริ่มต้นของคลาสนี้
  10. ใช้ตัวแก้ไขการเข้าถึงสาธารณะเพื่อทำเครื่องหมายฟังก์ชันที่เรากำลังจะสร้างว่าเข้าถึงได้แบบสาธารณะ จะสามารถเข้าถึงได้จากภายนอกชั้นเรียนนี้
  11. แทนที่ฟังก์ชัน eat() ที่กำหนดไว้ในคลาสพื้นฐาน { เป็นจุดเริ่มต้นของเนื้อหาฟังก์ชัน
  12. คำสั่งที่จะพิมพ์บนคอนโซลเมื่อมีการเรียกใช้ฟังก์ชันนี้
  13. ส่วนท้ายของฟังก์ชัน eat()
  14. จบร่างของคลาสวัว
  15. เรียกใช้ฟังก์ชัน main() { ถือเป็นจุดเริ่มต้นของเนื้อหาของฟังก์ชันนี้
  16. สร้างอินสแตนซ์ของคลาส Cow และตั้งชื่อให้ว่า c
  17. เรียกใช้ฟังก์ชัน eat() ที่กำหนดไว้ในคลาส Cow
  18. โปรแกรมจะต้องส่งคืนค่าเมื่อดำเนินการเสร็จสิ้น
  19. จุดสิ้นสุดของฟังก์ชัน main()

C++ ฟังก์ชั่นเสมือน

ฟังก์ชันเสมือนเป็นอีกวิธีหนึ่งในการนำโพลีมอร์ฟิซึมแบบรันไทม์มาใช้ใน C++เป็นฟังก์ชันพิเศษที่กำหนดในคลาสฐานและกำหนดใหม่ในคลาสที่สืบทอดมา หากต้องการประกาศฟังก์ชันเสมือน คุณควรใช้คีย์เวิร์ดเสมือน คีย์เวิร์ดควรอยู่ก่อนการประกาศฟังก์ชันในคลาสฐาน

หากคลาสฟังก์ชันเสมือนได้รับการสืบทอดมา คลาสเสมือนจะกำหนดฟังก์ชันเสมือนใหม่เพื่อให้เหมาะกับความต้องการ ตัวอย่างเช่น:

#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();      
	}

Output:

C++ ฟังก์ชั่นเสมือน

นี่คือภาพหน้าจอของรหัส:

C++ ฟังก์ชั่นเสมือน

คำอธิบายรหัส:

  1. รวมไฟล์ส่วนหัว iostream ไว้ในโค้ดเพื่อใช้ฟังก์ชันต่างๆ
  2. รวมเนมสเปซมาตรฐานไว้ในโค้ดของเราเพื่อใช้คลาสโดยไม่ต้องเรียกมัน
  3. สร้างคลาสชื่อ ClassA
  4. ใช้ตัวแก้ไขการเข้าถึงสาธารณะเพื่อทำเครื่องหมายสมาชิกชั้นเรียนว่าเข้าถึงได้แบบสาธารณะ
  5. สร้างฟังก์ชันเสมือนชื่อ show() มันจะเป็นงานสาธารณะ
  6. ข้อความที่จะพิมพ์เมื่อเรียกใช้ show() endl คือ 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()

ความแตกต่างระหว่างคอมไพล์กับเวลา ความแตกต่างระหว่างรันไทม์

นี่คือข้อแตกต่างที่สำคัญระหว่างทั้งสอง:

ความหลากหลายของเวลาคอมไพล์ ความหลากหลายแบบรันไทม์
เรียกอีกอย่างว่าการเชื่อมโยงตั้งแต่เนิ่นๆ หรือความหลากหลายแบบคงที่ เรียกอีกอย่างว่าการเชื่อมโยงแบบล่าช้า/ไดนามิกหรือความหลากหลายแบบไดนามิก
เมธอดนี้ถูกเรียก/เรียกใช้ระหว่างเวลาคอมไพล์ วิธีการนี้ถูกเรียก/เรียกใช้ในระหว่างรันไทม์
ใช้งานผ่านการโอเวอร์โหลดฟังก์ชันและการโอเวอร์โหลดตัวดำเนินการ ดำเนินการผ่านวิธีการแทนที่และฟังก์ชันเสมือน
ตัวอย่าง วิธีการโอเวอร์โหลด หลายวิธีอาจมีชื่อคล้ายกันแต่มีจำนวนหรือประเภทอาร์กิวเมนต์ต่างกัน ตัวอย่าง การเอาชนะวิธีการ หลายวิธีอาจมีชื่อคล้ายกันและมีต้นแบบเดียวกัน
การดำเนินการเร็วขึ้นเนื่องจากการค้นพบวิธีการเสร็จสิ้นในช่วงเวลาการคอมไพล์ การดำเนินการช้าลงเนื่องจากตัวค้นหาเมธอดเสร็จสิ้นระหว่างรันไทม์
Less มีความยืดหยุ่นในการแก้ไขปัญหาเนื่องจากทุกอย่างเป็นที่ทราบในระหว่างเวลาคอมไพล์ มีความยืดหยุ่นมากในการแก้ไขปัญหาที่ซับซ้อนเนื่องจากมีการค้นพบวิธีการระหว่างรันไทม์

สรุป

  • Polymorphism หมายถึง การมีหลายรูปแบบ
  • มันเกิดขึ้นเมื่อมีลำดับชั้นของคลาสที่เกี่ยวข้องผ่านการสืบทอด
  • ด้วยความหลากหลาย ฟังก์ชันสามารถทำงานได้แตกต่างกันไปตามวัตถุที่เรียกใช้/เรียกใช้ฟังก์ชันนั้น
  • ในความหลากหลายเวลาคอมไพล์ ฟังก์ชันที่จะเรียกใช้จะถูกสร้างขึ้นในช่วงเวลาคอมไพล์
  • ใน runtime polymorphism ฟังก์ชันที่จะเรียกใช้จะถูกสร้างขึ้นในระหว่างรันไทม์
  • ความแปรปรวนทางพันธุกรรมในเวลาคอมไพล์ถูกกำหนดโดยการโอเวอร์โหลดฟังก์ชันและการโอเวอร์โหลดตัวดำเนินการ
  • ในฟังก์ชันโอเวอร์โหลด มีฟังก์ชันมากมายที่มีชื่อคล้ายกันแต่มีอาร์กิวเมนต์ต่างกัน
  • พารามิเตอร์อาจแตกต่างกันตามจำนวนหรือประเภท
  • ในการโอเวอร์โหลดตัวดำเนินการ ความหมายใหม่ถูกกำหนดไว้สำหรับ C++ ผู้ประกอบการ.
  • ความหลากหลายแบบรันไทม์ทำได้โดยการเอาชนะฟังก์ชัน
  • ในการแทนที่ฟังก์ชัน คลาสที่ได้รับจะให้คำจำกัดความใหม่แก่ฟังก์ชันที่กำหนดไว้ในคลาสพื้นฐาน