C++ ความแตกต่างพร้อมตัวอย่าง
Polymorphism คืออะไร C++?
In C++โพลีมอร์ฟิซึมทำให้ฟังก์ชันสมาชิกมีพฤติกรรมแตกต่างกันไปขึ้นอยู่กับอ็อบเจ็กต์ที่เรียกใช้งาน โพลีมอร์ฟิซึมเป็นคำภาษากรีกที่แปลว่ามีหลายรูปแบบ เกิดขึ้นเมื่อคุณมีลำดับชั้นของคลาสที่เกี่ยวข้องกันผ่านการสืบทอด
ตัวอย่างเช่น สมมติว่าเรามีฟังก์ชัน makeSound() เมื่อแมวเรียกใช้ฟังก์ชันนี้ มันจะส่งเสียงร้องเหมียว เมื่อวัวเรียกใช้ฟังก์ชันเดียวกัน มันจะส่งเสียงวัว
แม้ว่าเราจะมีฟังก์ชันเดียว แต่ก็มีพฤติกรรมที่แตกต่างกันภายใต้สถานการณ์ที่ต่างกัน ฟังก์ชันมีหลายรูปแบบ ดังนั้นเราจึงประสบความสำเร็จในความหลากหลาย
ประเภทของความหลากหลาย
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:
นี่คือภาพหน้าจอของรหัส:
คำอธิบายรหัส:
- รวมไฟล์ส่วนหัว iostream ลงในโค้ดของเรา เราจะสามารถใช้ฟังก์ชั่นของมันได้
- รวมเนมสเปซมาตรฐานไว้ในโค้ดของเรา เราจะสามารถใช้คลาสของมันได้โดยไม่ต้องเรียกมัน
- สร้างฟังก์ชันชื่อ test ที่รับพารามิเตอร์จำนวนเต็ม i { เป็นจุดเริ่มต้นของการทดสอบเนื้อความของฟังก์ชัน
- คำสั่งที่จะดำเนินการหากมีการเรียกใช้/เรียกใช้การทดสอบฟังก์ชันข้างต้น
- ส่วนท้ายของการทดสอบการทำงานข้างต้น
- สร้างฟังก์ชันชื่อ test ที่ใช้พารามิเตอร์ float f { เป็นจุดเริ่มต้นของการทดสอบเนื้อความของฟังก์ชัน
- คำสั่งที่จะดำเนินการหากมีการเรียกใช้/เรียกใช้การทดสอบฟังก์ชันข้างต้น
- ส่วนท้ายของการทดสอบการทำงานข้างต้น
- สร้างฟังก์ชันชื่อ test ที่รับพารามิเตอร์อักขระ ch { เป็นจุดเริ่มต้นของการทดสอบเนื้อความของฟังก์ชัน
- คำสั่งที่จะดำเนินการหากมีการเรียกใช้/เรียกใช้การทดสอบฟังก์ชันข้างต้น
- ส่วนท้ายของการทดสอบการทำงานข้างต้น
- เรียกใช้ฟังก์ชัน main() { เป็นจุดเริ่มต้นของเนื้อความของฟังก์ชัน
- เรียกใช้การทดสอบฟังก์ชันและส่ง 5 ให้เป็นค่าของอาร์กิวเมนต์ ซึ่งจะเรียกใช้ฟังก์ชันทดสอบที่ยอมรับอาร์กิวเมนต์จำนวนเต็ม ซึ่งก็คือฟังก์ชันทดสอบแรก
- เรียกใช้การทดสอบฟังก์ชันและส่ง 5.5 ให้เป็นค่าของอาร์กิวเมนต์ สิ่งนี้จะเรียกใช้ฟังก์ชันทดสอบที่ยอมรับอาร์กิวเมนต์ float นั่นคือฟังก์ชันทดสอบที่สอง
- เรียกใช้การทดสอบฟังก์ชันและส่งค่าห้าให้เป็นค่าของอาร์กิวเมนต์ ซึ่งจะเรียกใช้ฟังก์ชันทดสอบที่ยอมรับอาร์กิวเมนต์อักขระ ซึ่งก็คือฟังก์ชันทดสอบตัวที่สาม
- โปรแกรมจะต้องส่งคืนค่าหากทำงานได้สำเร็จ
- ส่วนท้ายของฟังก์ชัน 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:
นี่คือภาพหน้าจอของรหัส:
คำอธิบายรหัส:
- รวมไฟล์ส่วนหัว iostream ลงในโปรแกรมของเราเพื่อใช้ฟังก์ชันต่างๆ
- รวมเนมสเปซมาตรฐานไว้ในโปรแกรมของเราเพื่อใช้คลาสโดยไม่ต้องเรียกมัน
- สร้างคลาสชื่อ ComplexNum เครื่องหมาย { จะเป็นจุดเริ่มต้นของตัวคลาส
- ใช้ตัวแก้ไขการเข้าถึงส่วนตัวเพื่อทำเครื่องหมายตัวแปรว่าเป็นส่วนตัว ซึ่งหมายความว่าสามารถเข้าถึงได้จากภายในชั้นเรียนเท่านั้น
- กำหนดตัวแปรจำนวนเต็มสองตัว จริงและมากกว่า
- ใช้ตัวแก้ไขการเข้าถึงสาธารณะเพื่อทำเครื่องหมายตัวสร้างให้เป็นสาธารณะ ซึ่งหมายความว่าจะสามารถเข้าถึงได้แม้จากภายนอก ชั้น.
- สร้างตัวสร้างคลาสและการเริ่มต้นตัวแปร
- เริ่มต้นค่าของตัวแปรจริง
- เริ่มต้นค่าของตัวแปรทับ
- จุดสิ้นสุดของตัวสร้าง
- เราจำเป็นต้องแทนที่ความหมายของตัวดำเนินการ +
- สร้างผลลัพธ์ชนิดข้อมูลประเภท ComplexNum
- ใช้ตัวดำเนินการ + กับตัวเลขเชิงซ้อน บรรทัดนี้จะเพิ่มส่วนจริงของตัวเลขเข้ากับส่วนจริงของตัวเลขอื่น
- ใช้ตัวดำเนินการ + กับตัวเลขเชิงซ้อน บรรทัดนี้จะเพิ่มส่วนจินตภาพของตัวเลขเข้ากับส่วนจินตภาพของตัวเลขอื่น
- โปรแกรมจะคืนค่าของผลลัพธ์ของตัวแปรเมื่อดำเนินการสำเร็จ
- สิ้นสุดนิยามความหมายใหม่ของตัวดำเนินการ + นั่นคือการโอเวอร์โหลด
- เรียกเมธอด print()
- พิมพ์ตัวเลขเชิงซ้อนใหม่หลังจากการบวกบนคอนโซล
- ส่วนท้ายของฟังก์ชัน print()
- จุดสิ้นสุดของเนื้อหาคลาส ComplexNum
- เรียกใช้ฟังก์ชัน main()
- ส่งค่าทั้งส่วนจริงและส่วนเชิงซ้อนที่ต้องการบวก โดยส่วนแรกของ c1 จะถูกบวกกับส่วนแรกของ c2 นั่นคือ 10+3 ส่วนที่สองของ c1 จะถูกบวกกับส่วนที่สองของ c นั่นคือ 2+7
- ดำเนินการโดยใช้ตัวดำเนินการโอเวอร์โหลด + และเก็บผลลัพธ์ไว้ในตัวแปร c3
- พิมพ์ค่าของตัวแปร c3 บนคอนโซล
- ส่วนท้ายของฟังก์ชัน 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:
นี่คือภาพหน้าจอของรหัส:
คำอธิบายรหัส:
- นำเข้าไฟล์ส่วนหัว iostream ลงในโปรแกรมของเราเพื่อใช้ฟังก์ชันต่างๆ
- รวมเนมสเปซมาตรฐานไว้ในโปรแกรมของเราเพื่อใช้คลาสโดยไม่ต้องเรียกมัน
- สร้างคลาสชื่อ Mammal { ถือเป็นจุดเริ่มต้นของเนื้อหาคลาส
- ใช้ตัวแก้ไขการเข้าถึงสาธารณะเพื่อตั้งค่าฟังก์ชันที่เรากำลังจะสร้างให้เข้าถึงได้แบบสาธารณะ จะสามารถเข้าถึงได้จากภายนอกชั้นเรียนนี้
- สร้างฟังก์ชั่นสาธารณะชื่อกิน { เป็นจุดเริ่มต้นของเนื้อหาฟังก์ชัน
- พิมพ์คำสั่งที่เพิ่มเข้าไปในฟังก์ชัน cout เมื่อเรียกใช้ฟังก์ชัน eat()
- ส่วนท้ายของฟังก์ชัน eat()
- ปิดท้ายด้วยคลาส Mammal
- สร้างคลาสชื่อ Cow ที่สืบทอดคลาส Mammal Cow เป็นคลาสที่ได้รับ ในขณะที่ Mammal เป็นคลาสพื้นฐาน { ถือเป็นจุดเริ่มต้นของคลาสนี้
- ใช้ตัวแก้ไขการเข้าถึงสาธารณะเพื่อทำเครื่องหมายฟังก์ชันที่เรากำลังจะสร้างว่าเข้าถึงได้แบบสาธารณะ จะสามารถเข้าถึงได้จากภายนอกชั้นเรียนนี้
- แทนที่ฟังก์ชัน eat() ที่กำหนดไว้ในคลาสพื้นฐาน { เป็นจุดเริ่มต้นของเนื้อหาฟังก์ชัน
- คำสั่งที่จะพิมพ์บนคอนโซลเมื่อมีการเรียกใช้ฟังก์ชันนี้
- ส่วนท้ายของฟังก์ชัน eat()
- จบร่างของคลาสวัว
- เรียกใช้ฟังก์ชัน main() { ถือเป็นจุดเริ่มต้นของเนื้อหาของฟังก์ชันนี้
- สร้างอินสแตนซ์ของคลาส Cow และตั้งชื่อให้ว่า c
- เรียกใช้ฟังก์ชัน eat() ที่กำหนดไว้ในคลาส Cow
- โปรแกรมจะต้องส่งคืนค่าเมื่อดำเนินการเสร็จสิ้น
- จุดสิ้นสุดของฟังก์ชัน 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:
นี่คือภาพหน้าจอของรหัส:
คำอธิบายรหัส:
- รวมไฟล์ส่วนหัว iostream ไว้ในโค้ดเพื่อใช้ฟังก์ชันต่างๆ
- รวมเนมสเปซมาตรฐานไว้ในโค้ดของเราเพื่อใช้คลาสโดยไม่ต้องเรียกมัน
- สร้างคลาสชื่อ ClassA
- ใช้ตัวแก้ไขการเข้าถึงสาธารณะเพื่อทำเครื่องหมายสมาชิกชั้นเรียนว่าเข้าถึงได้แบบสาธารณะ
- สร้างฟังก์ชันเสมือนชื่อ show() มันจะเป็นงานสาธารณะ
- ข้อความที่จะพิมพ์เมื่อเรียกใช้ show() endl คือ C++ คำสำคัญซึ่งหมายถึงบรรทัดสิ้นสุด มันเลื่อนเคอร์เซอร์ของเมาส์ไปที่บรรทัดถัดไป
- ส่วนท้ายของฟังก์ชันเสมือน show()
- ส่วนท้ายของคลาส ClassA
- การสร้างคลาสใหม่ชื่อ ClassB ที่สืบทอดคลาส ClassA ClassA กลายเป็นคลาสพื้นฐานในขณะที่ ClassB กลายเป็นคลาสที่ได้รับ
- ใช้ตัวแก้ไขการเข้าถึงสาธารณะเพื่อทำเครื่องหมายสมาชิกชั้นเรียนว่าเข้าถึงได้แบบสาธารณะ
- กำหนดฟังก์ชันเสมือน show() ที่ได้รับในคลาสพื้นฐานใหม่
- ข้อความที่จะพิมพ์บนคอนโซลเมื่อมีการเรียกใช้ฟังก์ชัน show() ที่กำหนดไว้ในคลาสที่ได้รับ
- ส่วนท้ายของฟังก์ชัน show()
- ส่วนท้ายของคลาสที่ได้รับ ClassB
- เรียกใช้ฟังก์ชัน main() ควรเพิ่มตรรกะของโปรแกรมภายในเนื้อหา
- สร้างตัวแปรพอยน์เตอร์ชื่อ a มันชี้ไปที่คลาสชื่อ ClassA
- สร้างอินสแตนซ์ของคลาสชื่อ ClassB อินสแตนซ์ได้รับชื่อ b
- กำหนดค่าที่จัดเก็บไว้ในที่อยู่ b ในตัวแปร a
- เรียกใช้ฟังก์ชัน show() ที่กำหนดไว้ในคลาสที่ได้รับ มีผลผูกพันล่าช้า
- ส่วนท้ายของฟังก์ชัน main()
ความแตกต่างระหว่างคอมไพล์กับเวลา ความแตกต่างระหว่างรันไทม์
นี่คือข้อแตกต่างที่สำคัญระหว่างทั้งสอง:
ความหลากหลายของเวลาคอมไพล์ | ความหลากหลายแบบรันไทม์ |
---|---|
เรียกอีกอย่างว่าการเชื่อมโยงตั้งแต่เนิ่นๆ หรือความหลากหลายแบบคงที่ | เรียกอีกอย่างว่าการเชื่อมโยงแบบล่าช้า/ไดนามิกหรือความหลากหลายแบบไดนามิก |
เมธอดนี้ถูกเรียก/เรียกใช้ระหว่างเวลาคอมไพล์ | วิธีการนี้ถูกเรียก/เรียกใช้ในระหว่างรันไทม์ |
ใช้งานผ่านการโอเวอร์โหลดฟังก์ชันและการโอเวอร์โหลดตัวดำเนินการ | ดำเนินการผ่านวิธีการแทนที่และฟังก์ชันเสมือน |
ตัวอย่าง วิธีการโอเวอร์โหลด หลายวิธีอาจมีชื่อคล้ายกันแต่มีจำนวนหรือประเภทอาร์กิวเมนต์ต่างกัน | ตัวอย่าง การเอาชนะวิธีการ หลายวิธีอาจมีชื่อคล้ายกันและมีต้นแบบเดียวกัน |
การดำเนินการเร็วขึ้นเนื่องจากการค้นพบวิธีการเสร็จสิ้นในช่วงเวลาการคอมไพล์ | การดำเนินการช้าลงเนื่องจากตัวค้นหาเมธอดเสร็จสิ้นระหว่างรันไทม์ |
Less มีความยืดหยุ่นในการแก้ไขปัญหาเนื่องจากทุกอย่างเป็นที่ทราบในระหว่างเวลาคอมไพล์ | มีความยืดหยุ่นมากในการแก้ไขปัญหาที่ซับซ้อนเนื่องจากมีการค้นพบวิธีการระหว่างรันไทม์ |
สรุป
- Polymorphism หมายถึง การมีหลายรูปแบบ
- มันเกิดขึ้นเมื่อมีลำดับชั้นของคลาสที่เกี่ยวข้องผ่านการสืบทอด
- ด้วยความหลากหลาย ฟังก์ชันสามารถทำงานได้แตกต่างกันไปตามวัตถุที่เรียกใช้/เรียกใช้ฟังก์ชันนั้น
- ในความหลากหลายเวลาคอมไพล์ ฟังก์ชันที่จะเรียกใช้จะถูกสร้างขึ้นในช่วงเวลาคอมไพล์
- ใน runtime polymorphism ฟังก์ชันที่จะเรียกใช้จะถูกสร้างขึ้นในระหว่างรันไทม์
- ความแปรปรวนทางพันธุกรรมในเวลาคอมไพล์ถูกกำหนดโดยการโอเวอร์โหลดฟังก์ชันและการโอเวอร์โหลดตัวดำเนินการ
- ในฟังก์ชันโอเวอร์โหลด มีฟังก์ชันมากมายที่มีชื่อคล้ายกันแต่มีอาร์กิวเมนต์ต่างกัน
- พารามิเตอร์อาจแตกต่างกันตามจำนวนหรือประเภท
- ในการโอเวอร์โหลดตัวดำเนินการ ความหมายใหม่ถูกกำหนดไว้สำหรับ C++ ผู้ประกอบการ.
- ความหลากหลายแบบรันไทม์ทำได้โดยการเอาชนะฟังก์ชัน
- ในการแทนที่ฟังก์ชัน คลาสที่ได้รับจะให้คำจำกัดความใหม่แก่ฟังก์ชันที่กำหนดไว้ในคลาสพื้นฐาน