คุณกำลังเข้าสู่สมมุติฐานกับคำตอบเหล่านี้ดังนั้นฉันจะพยายามทำให้คำอธิบายที่เรียบง่ายขึ้นและลงสู่พื้นโลกมากขึ้นเพื่อประโยชน์ของความชัดเจน
ความสัมพันธ์พื้นฐานของการออกแบบเชิงวัตถุคือสอง: IS-A และ HAS-A ฉันไม่ได้ทำสิ่งเหล่านั้นขึ้นมา นั่นคือสิ่งที่พวกเขาถูกเรียก
IS-A ระบุว่าวัตถุใดวัตถุหนึ่งระบุว่าเป็นของคลาสที่อยู่เหนือวัตถุนั้นในลำดับชั้นของคลาส วัตถุกล้วยเป็นวัตถุผลไม้หากเป็นคลาสย่อยของคลาสผลไม้ ซึ่งหมายความว่าสามารถใช้คลาสผลไม้ได้ทุกที่สามารถใช้กล้วยได้ แม้ว่ามันจะไม่ได้สะท้อน คุณไม่สามารถแทนที่คลาสพื้นฐานสำหรับคลาสเฉพาะได้ถ้าคลาสนั้นนั้นถูกเรียกใช้
Has-a ระบุว่าวัตถุเป็นส่วนหนึ่งของคลาสประกอบและมีความสัมพันธ์ความเป็นเจ้าของ มันหมายถึงใน C ++ ว่ามันเป็นวัตถุสมาชิกและเป็นเช่นนั้นความรับผิดชอบอยู่ในระดับที่เป็นเจ้าของในการกำจัดมันหรือมือเป็นเจ้าของออกไปก่อนที่จะทำลายตัวเอง
แนวคิดทั้งสองนี้ง่ายต่อการรับรู้ในภาษาที่สืบทอดเดียวมากกว่าในรูปแบบการสืบทอดหลายอย่างเช่น c ++ แต่กฎเป็นเหมือนกัน ภาวะแทรกซ้อนเกิดขึ้นเมื่อเอกลักษณ์ของคลาสไม่ชัดเจนเช่นการส่งตัวชี้คลาสกล้วยไปยังฟังก์ชันที่ใช้ตัวชี้คลาสผลไม้
ฟังก์ชั่นเสมือนเป็นสิ่งแรกที่ต้องทำ มันเป็นส่วนหนึ่งของความหลากหลายในการที่จะใช้ในการตัดสินใจว่าฟังก์ชั่นที่จะทำงานในเวลาที่มันถูกเรียกในโปรแกรมที่กำลังทำงานอยู่
คำหลักเสมือนเป็นคอมไพเลอร์คำสั่งเพื่อผูกฟังก์ชั่นในลำดับที่แน่นอนถ้ามีความกำกวมเกี่ยวกับตัวตนของชั้น ฟังก์ชั่นเสมือนอยู่ในคลาสพาเรนต์เสมอ (เท่าที่ฉันรู้) และบ่งบอกถึงคอมไพเลอร์ที่เชื่อมโยงฟังก์ชันสมาชิกกับชื่อของพวกเขาควรจะเกิดขึ้นกับฟังก์ชั่นคลาสย่อยก่อนและฟังก์ชั่นระดับผู้ปกครองหลังจากนั้น
คลาส Fruit อาจมีสีฟังก์ชันเสมือน () ที่ส่งคืน "NONE" เป็นค่าเริ่มต้น ฟังก์ชันคลาส Banana () สีคืนค่า "YELLOW" หรือ "BROWN"
แต่ถ้าฟังก์ชั่นการใช้ตัวชี้ผลไม้เรียกสี () บนคลาส Banana ที่ส่งไป - ฟังก์ชัน color () ใดที่ถูกเรียกใช้? ฟังก์ชั่นปกติจะเรียกผลไม้ :: สี () สำหรับวัตถุผลไม้
นั่นจะ 99% ของเวลาไม่ใช่สิ่งที่ตั้งใจไว้ แต่ถ้า Fruit :: color () ถูกประกาศเสมือนแล้ว Banana: color () จะถูกเรียกใช้สำหรับวัตถุเนื่องจากฟังก์ชัน color () ที่ถูกต้องจะถูกผูกไว้กับตัวชี้ Fruit ในขณะที่มีการโทร รันไทม์จะตรวจสอบวัตถุที่ตัวชี้ชี้ไปเนื่องจากถูกทำเครื่องหมายเสมือนในนิยามคลาสผลไม้
สิ่งนี้แตกต่างจากการแทนที่ฟังก์ชันในคลาสย่อย ในกรณีนั้นตัวชี้ผลไม้จะเรียกผลไม้ :: สี () ถ้าทั้งหมดรู้ก็คือมันเป็นตัวชี้ไปที่ผลไม้
ดังนั้นแนวคิดของ "ฟังก์ชั่นเสมือนจริง" จึงเกิดขึ้น มันเป็นวลีที่ค่อนข้างโชคร้ายเนื่องจากความบริสุทธิ์ไม่มีส่วนเกี่ยวข้องกับมัน หมายความว่ามันมีจุดมุ่งหมายที่จะไม่เรียกวิธีการคลาสพื้นฐาน อันที่จริงไม่สามารถเรียกใช้ฟังก์ชันเสมือนบริสุทธิ์ได้ อย่างไรก็ตามจะต้องมีการกำหนดไว้ ต้องมีลายเซ็นฟังก์ชัน ผู้เขียนโค้ดจำนวนมากทำการใช้งานที่ว่าง {} เพื่อความสมบูรณ์ แต่คอมไพเลอร์จะสร้างหนึ่งภายในหากไม่ได้ ในกรณีนั้นเมื่อฟังก์ชั่นถูกเรียกแม้ว่าตัวชี้ไปที่ Fruit, Banana :: color () จะถูกเรียกใช้เนื่องจากเป็นการใช้สีเท่านั้น () ที่มี
ตอนนี้ชิ้นส่วนสุดท้ายของจิ๊กซอว์: ผู้สร้างและผู้ทำลายล้าง
Constructor เสมือนล้วนผิดกฎหมายอย่างสมบูรณ์ นั่นเพิ่งจะออกมา
แต่ destructors เสมือนล้วนทำงานในกรณีที่คุณต้องการห้ามการสร้างอินสแตนซ์คลาสพื้นฐาน คลาสย่อยเท่านั้นที่สามารถสร้างอินสแตนซ์ได้หากตัวทำลายของคลาสฐานนั้นเป็นเสมือนจริง การประชุมคือการกำหนดให้เป็น 0
virtual ~Fruit() = 0; // pure virtual
Fruit::~Fruit(){} // destructor implementation
คุณต้องสร้างการนำไปใช้ในกรณีนี้ คอมไพเลอร์รู้ว่านี่คือสิ่งที่คุณกำลังทำและทำให้แน่ใจว่าคุณทำถูกต้องหรือบ่นอย่างมากว่าไม่สามารถเชื่อมโยงไปยังฟังก์ชั่นทั้งหมดที่จำเป็นในการรวบรวม ข้อผิดพลาดอาจเกิดความสับสนหากคุณไม่ได้อยู่ในแนวทางที่ถูกต้องเกี่ยวกับวิธีการสร้างแบบจำลองลำดับชั้นของคุณ
ดังนั้นคุณจะถูกห้ามในกรณีนี้เพื่อสร้างอินสแตนซ์ของ Fruit แต่ได้รับอนุญาตให้สร้างอินสแตนซ์ของ Banana
การเรียกการลบตัวชี้ผลไม้ที่ชี้ไปยังอินสแตนซ์ของ Banana จะเรียก Banana :: ~ Banana () ก่อนแล้วจึงเรียก Fuit :: ~ Fruit () เสมอ เนื่องจากไม่ว่าจะเกิดอะไรขึ้นเมื่อคุณเรียก subclass destructor ตัวทำลายคลาสพื้นฐานต้องปฏิบัติตาม
มันเป็นรูปแบบที่ไม่ดีหรือไม่? มันมีความซับซ้อนมากขึ้นในขั้นตอนการออกแบบใช่ แต่สามารถมั่นใจได้ว่าการเชื่อมโยงที่ถูกต้องจะดำเนินการในเวลาทำงานและฟังก์ชั่นคลาสย่อยจะดำเนินการในกรณีที่มีความคลุมเครือในการเข้าถึงคลาสย่อยที่แน่นอน
ถ้าคุณเขียน C ++ เพื่อให้คุณส่งผ่านพอยน์เตอร์พอยน์เตอร์ที่แน่นอนโดยไม่ต้องพอยน์เตอร์ทั่วไปหรือคลุมเครือดังนั้นฟังก์ชันเสมือนไม่จำเป็น แต่ถ้าคุณต้องการความยืดหยุ่นในการใช้งานประเภท (เช่นใน Apple Banana Orange ==> ผลไม้) ฟังก์ชั่นจะง่ายขึ้นและหลากหลายมากขึ้นด้วยรหัสซ้ำซ้อนน้อยลง คุณไม่จำเป็นต้องเขียนฟังก์ชันสำหรับผลไม้แต่ละชนิดอีกต่อไปและคุณรู้ว่าผลไม้ทุกชนิดจะตอบสนองต่อสี () ด้วยฟังก์ชั่นที่ถูกต้องของตัวเอง
ฉันหวังว่าคำอธิบายที่ยืดยาวนี้จะทำให้แนวความคิดมั่นคงมากกว่าที่จะสับสน มีตัวอย่างที่ดีมากมายให้ดูและดูให้เพียงพอและใช้งานจริงและยุ่งกับพวกเขาและคุณจะได้รับ