สายทาส

I. หลักการพื้นฐานของ OOP

1. การห่อหุ้ม– หลักการของ OOP ซึ่งประกอบด้วยการรวมข้อมูลและอัลกอริธึมเพื่อประมวลผลให้เป็นหนึ่งเดียว

ข้อมูลวัตถุใน OOP เรียกว่า เขตข้อมูลวัตถุ และอัลกอริธึม เช่น การดำเนินการกับข้อมูลออบเจ็กต์ จะถูกเรียก วิธีการวัตถุ ซึ่งได้รับการออกแบบในรูปแบบของรูทีนย่อย

2. มรดก– หลักการของ OOP ซึ่งประกอบด้วยคุณสมบัติของวัตถุเพื่อสร้างลูกหลาน

วัตถุลูกสืบทอดฟิลด์และวิธีการทั้งหมดจากพาเรนต์โดยอัตโนมัติ สามารถเสริมอ็อบเจ็กต์ด้วยฟิลด์ใหม่และแทนที่หรือเสริมเมธอดพาเรนต์ได้

3. ความแตกต่าง- นี่คือคุณสมบัติของวัตถุที่มีพาเรนต์ร่วมกันเพื่อแก้ไขปัญหาที่มีความหมายคล้ายกันในรูปแบบที่แตกต่างกัน

วิธีการที่หลากหลายจะแสดงต่อหน้าวิธีการที่มีชื่อเดียวกันในบรรพบุรุษและลูกหลาน แต่มีการใช้งานที่แตกต่างกัน

ครั้งที่สอง โครงสร้างของการประกาศประเภทวัตถุ

<имя_типа>=วัตถุ

<имя_поля>:<тип_поля>;

<методы>;

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

<имя_типа>.<имя_подпрограммы>

III. ทรัพย์สินทางมรดก

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

กฎเกณฑ์การรับมรดก

1. ในคำจำกัดความของประเภททายาท ไม่ควรมีฟิลด์ที่มีชื่อเหมือนกับฟิลด์ของผู้ปกครอง ชื่อของวิธีการของทายาทและผู้ปกครองอาจจะเหมือนกัน - นี่คือที่ประจักษ์ คุณสมบัติของความหลากหลาย .

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

คุณสมบัติของความหลากหลาย

คุณสมบัติของความหลากหลายคือในคำจำกัดความของประเภททายาทอาจมีวิธีการชื่อเดียวกันกับชื่อของวิธีการหลัก

กฎการสืบทอดสำหรับความหลากหลาย:

· วิธีการที่มีชื่อเดียวกันในทายาทจะแทนที่วิธีการของผู้ปกครอง

· วิธีการทายาทที่ไม่มีชื่อเดียวกันกับวิธีการของผู้ปกครองจะถูกเพิ่มหลังวิธีการของผู้ปกครอง

IV. วิธีการเสมือน

วิธีการของวัตถุคือ โดยใช้วิธีการคงที่ หากคอมไพลเลอร์วางและแก้ไขการอ้างอิงทั้งหมด ณ เวลาคอมไพล์และลิงก์ กระบวนการที่การเรียกไปยังเมธอดแบบสแตติกได้รับการแก้ไขโดยเฉพาะโดยคอมไพเลอร์ในเวลาคอมไพล์เรียกว่า มีผลผูกพันตั้งแต่เนิ่นๆ .

ด้วยการผูกสายผู้โทรและวิธีการเรียกไม่สามารถเชื่อมโยงด้วยที่อยู่ในเวลารวบรวมได้ ดังนั้นจึงรวมกลไกเพื่อให้การเชื่อมโยงเกิดขึ้นในขณะที่เกิดการเรียกจริง นั่นคือระหว่างการทำงานของโปรแกรม

วิธีการเสมือนและตารางวิธีการเสมือน (TVM)

การรวมล่าช้าถูกนำมาใช้โดยใช้ วิธีการเสมือน ซึ่งเป็นสัญญาณของการมีอยู่ของคำหลัก เสมือนหลังส่วนหัวของวิธีการในคำนิยามประเภทวัตถุ

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

ตัวสร้าง

วิธีคอนสตรัคเตอร์

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

Constructor สามารถเป็นได้เฉพาะโพรซีเดอร์ที่ส่วนหัวมีคำฟังก์ชันเท่านั้น ขั้นตอนจะต้องเขียน คอนสตรัคเตอร์

ตัวสร้างไม่สามารถเป็นเสมือนได้

หลังจากดำเนินการเมธอด Constructor แล้ว เมื่อเรียกใช้เมธอดเสมือน ที่อยู่จะถูกกำหนดจาก TVM

ผู้ทำลายล้าง

ผู้ทำลายล้างทำหน้าที่ล้างหน่วยความจำและลบวัตถุที่จัดสรรแบบไดนามิก โดยทั่วไปแล้ว destructor จะระบุการดำเนินการขั้นสุดท้ายที่จะดำเนินการกับออบเจ็กต์ มีคำบริการอยู่ในส่วนหัวของ destructor ขั้นตอนถูกแทนที่ด้วย ผู้ทำลายล้างคุณสามารถกำหนดตัวทำลายหลายตัวสำหรับวัตถุประเภทเดียวกันได้ ตัวทำลายสามารถสืบทอดมาได้และเป็นแบบคงที่หรือเสมือนก็ได้

V. ตัวอย่างประเภทวัตถุ

1. คำชี้แจงปัญหา:ใช้การเคลื่อนไหวของจุดบนหน้าจอ

2. แบบจำลองทางคณิตศาสตร์:แต่ละจุดบนหน้าจอมีลักษณะเป็นพิกัด เอ็กซ์, ยและสภาพ โวลต์– มองเห็นได้/มองไม่เห็น.

3. การประกาศจุดประเภทวัตถุ:

จุด=วัตถุ

X, Y: จำนวนเต็ม; (พิกัด)

V: บูลีน; (เครื่องหมายการมองเห็น: มองเห็น TRUE; มองไม่เห็น FALSE)

ฟังก์ชัน GET_X: จำนวนเต็ม; (รับพิกัด X)

ฟังก์ชัน GET_Y: จำนวนเต็ม; (รับพิกัด Y)

ฟังก์ชั่น GET_V: บูลีน; (ได้รับสัญญาณการมองเห็น)

ขั้นตอน INIT(X0, Y0: จำนวนเต็ม); (กำหนดพิกัด)

ขั้นตอน TURN_ON; (วาดจุด - ได้จุดที่มองเห็นได้)

ขั้นตอน TURN_OFF; (ลบจุด-ได้จุดที่มองไม่เห็น)

ขั้นตอนการย้าย (XN, YN: จำนวนเต็ม); (ย้ายจุด)

4. การกำหนดวิธีการของวัตถุ POINT:

ฟังก์ชั่น POINT.GET_X; (รับพิกัด X)

GET_X:=X;

ฟังก์ชั่น POINT.GET_Y; (รับพิกัด Y)

GET_Y:=ป;

ฟังก์ชั่น POINT.GET_V; (ได้รับสัญญาณการมองเห็น)

GET_V:=วี;

ขั้นตอน POINT.INIT; (กำหนดพิกัด)

วี:=เท็จ;

ขั้นตอน POINT.TURN_ON; (ได้จุดที่มองเห็นได้)

ถ้าไม่ใช่วีล่ะก็

ใส่พิกเซล(X,Y,GetColor); (GetColor ส่งกลับสีปัจจุบัน)

วี:=จริง;

ขั้นตอน POINT.TURN_OFF; (ได้จุดที่มองไม่เห็น-ลบจุด)

ถ้าวีแล้ว

ใส่พิกเซล(X,Y,GetBkColor); (GetBkColor ส่งกลับสีพื้นหลัง)

วี:=เท็จ;

ขั้นตอน POINT.MOVE; (ย้ายจุด)

F: บูลีน;

ถ้า F แล้ว

TURN_OFF;

ถ้า F แล้ว

TURN_ON;

ทรัพย์สินทางมรดก

นอกจากจุดแล้ว คุณยังสามารถระบุวัตถุอื่นๆ ที่อธิบายรูปทรงเรขาคณิตได้ เช่น วัตถุประเภท วงกลมซึ่งกำหนดไว้แล้ว รัศมี, พิกัดกึ่งกลาง, ตัวแสดงการมองเห็นและการกระทำเดียวกันนี้เป็นไปได้เช่นเดียวกับจุด: การได้วงกลมที่มองเห็นได้ ได้รับวงกลมที่มองไม่เห็น การย้ายวงกลม (เปลี่ยนพิกัดของศูนย์กลาง) หากต้องการสแนปวัตถุนี้เข้ากับตารางหน้าจอ คุณต้องกำหนดประเภทวัตถุ สถานที่) ซึ่งจะมีข้อมูลร่วมกันสำหรับรูปทรงเรขาคณิตทั้งหมด - พิกัดของวัตถุที่แนบกับหน้าจอ เอ็กซ์และ :

สถานที่=วัตถุ

X, Y: จำนวนเต็ม;

ฟังก์ชัน GET_X: จำนวนเต็ม;

ฟังก์ชัน GET_Y: จำนวนเต็ม;

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

การใช้ประเภท สถานที่ในฐานะพาเรนต์ คุณสามารถกำหนดประเภทออบเจ็กต์ได้ จุดดังนั้น:

POINT=วัตถุ(สถานที่)

V: บูลีน;

ขั้นตอน INIT(X0, Y0: จำนวนเต็ม);

ฟังก์ชัน GET_V:บูลีน;

ขั้นตอน TURN_ON;

ขั้นตอน TURN_OFF;

6. คุณสมบัติของความหลากหลายคือในการประกาศประเภทลูกหลานนั้นสามารถมีวิธีการที่มีชื่อเดียวกันกับชื่อของวิธีการหลักได้

ประกาศประเภท วงกลมโดยใช้ type เป็นพาเรนต์ จุดจะเป็นดังนี้:

CIRCL=วัตถุ(จุด)

R:จำนวนเต็ม;

ขั้นตอน INIT(X0, Y0, R0: จำนวนเต็ม); (ข้อกำหนดวงกลม)

ขั้นตอน TURN_ON; (ได้วงกลมที่มองเห็นได้)

ขั้นตอน TURN_OFF; (ได้วงกลมที่มองไม่เห็น)

ขั้นตอนการย้าย (XN, YN: จำนวนเต็ม); (การเคลื่อนที่เป็นวงกลม)

ฟังก์ชัน GET_R: จำนวนเต็ม; (รับรัศมี)

การกำหนดวิธีการโดยใช้มรดกที่มีความหลากหลาย:

ขั้นตอน CIRCL.INIT;

เอ็กซ์:=X0; ย:=Y0;

วี:=เท็จ;

ขั้นตอน CIRCL.TURN_ON;

วี:=จริง;

วงกลม(X,Y,R);

ขั้นตอน CIRCL.TURN_OFF;

C: ไบต์;

C:=รับสี;

SetColor(GetBkColor); (การตั้งค่าสีการวาด)

วงกลม(X,Y,R);

วี:=เท็จ;

ตั้งค่าสี(C);

ขั้นตอน CIRCL.MOVE;

F: บูลีน;

ถ้า F แล้ว

TURN_OFF;

ถ้า F แล้ว

TURN_ON;

ฟังก์ชั่น CIRCL.GET_R;

GET_R:=ร;

จึงทำให้ข้อความของขั้นตอนดังกล่าว วงกลมย้ายตรงกับข้อความของขั้นตอน จุด.ย้ายแต่รหัสเครื่องสำหรับขั้นตอนเหล่านี้จะแตกต่างออกไป วงกลมย้ายในระหว่างการดำเนินการจะอ้างอิงถึงที่อยู่ของขั้นตอนต่างๆ CIRCL.TURN_ONและ CIRCL.TURN_OFFและขั้นตอน จุด.ย้าย– ไปยังที่อยู่ขั้นตอน จุดTURN_ONและ จุดTURN_OFF.

การเชื่อมโยงวิธีต้นและปลาย

ทบทวนไปแล้ว วิธีการคงที่ คอมไพเลอร์จะวางและแก้ไขการอ้างอิงทั้งหมด ณ เวลาคอมไพล์และลิงก์ ( มีผลผูกพันตั้งแต่เนิ่นๆ ).

สำหรับตัวอย่างที่อธิบายไว้ก่อนหน้านี้ในวัตถุ วงกลมคุณสามารถสืบทอดวิธีการได้ เคลื่อนไหวที่วัตถุ จุด.วิธีการ TURN_ONและ TURN_OFFจะต้องประกาศเสมือนจึงจะผูกมัดได้ในภายหลัง

คอมไพเลอร์ทิ้งการอ้างอิงที่ยังไม่ได้รับการแก้ไขไปยังวิธีการเหล่านั้นที่ถูกประกาศเสมือนหลังจากการคอมไพล์ สำหรับตัวอย่างที่อธิบายไว้ก่อนหน้านี้ในวิธีการ เคลื่อนไหวการอ้างอิงที่ไม่ได้รับการแก้ไขจะเป็นที่อยู่ของวิธีการ TURN_ON, TURN_OFFเช่น ขั้นตอน เคลื่อนไหวจะไม่พร้อมสำหรับการดำเนินการหลังจากการคอมไพล์ เนื่องจากรหัสเครื่องไม่ได้กำหนดไว้อย่างสมบูรณ์

ในระหว่างการทำงานของโปรแกรม ก่อนที่จะดำเนินการวิธีการเสมือนของอ็อบเจ็กต์ใด ๆ จะต้องดำเนินการดังต่อไปนี้: วิธีคอนสตรัคเตอร์ สำหรับอินสแตนซ์ที่กำหนดของออบเจ็กต์ที่คอมไพลเลอร์แทรกการดำเนินการเพื่อเชื่อมโยงการอ้างอิงที่ยังไม่ได้แก้ไขกับ TVM

การประกาศประเภทวัตถุ สถานที่ จุดและ วงกลมโดยใช้ นักออกแบบ และวิธีการเสมือน:

สถานที่=วัตถุ

X,Y: จำนวนเต็ม;

ฟังก์ชัน GET_X: จำนวนเต็ม;

ฟังก์ชัน GET_Y: จำนวนเต็ม;

POINT=วัตถุ(สถานที่)

V: บูลีน;

ตัวสร้าง INIT (X0, Y0: จำนวนเต็ม);

ฟังก์ชั่น GET_V: บูลีน;

ขั้นตอน TURN_ON; เสมือน;

ขั้นตอน TURN_OFF; เสมือน;

ขั้นตอนการย้าย (XN, YN: จำนวนเต็ม);

CIRCL=วัตถุ(จุด)

R:จำนวนเต็ม;

ตัวสร้าง INIT (X0, Y0, R0: จำนวนเต็ม);

ฟังก์ชัน GET_R: จำนวนเต็ม;

ขั้นตอน TURN_ON; เสมือน;

ขั้นตอน TURN_OFF; เสมือน;

ผู้ทำลายล้างจำเป็นสำหรับอ็อบเจ็กต์ที่จัดสรรแบบไดนามิกเท่านั้น เนื่องจากจะกำหนดขนาดที่แท้จริงของอ็อบเจ็กต์เพื่อจัดสรรหน่วยความจำจากอ็อบเจ็กต์ไดนามิกอย่างถูกต้อง และการดำเนินการเหล่านี้จะดำเนินการโดย destructor เริ่มต้น นอกเหนือจากการดำเนินการที่อธิบายไว้ในเนื้อความของ destructor


2

สมมติว่าไม่มีฟังก์ชัน Hello และเราเพียงแค่เรียก ob.display โดยพื้นฐานแล้วมันจะเรียกฟังก์ชัน display ของคลาส B ไม่ใช่คลาส A

การเรียกเพื่อแสดง () ถูกตั้งค่าหนึ่งครั้งโดยคอมไพเลอร์เป็นเวอร์ชันที่กำหนดไว้ในคลาสพื้นฐาน สิ่งนี้เรียกว่าการแก้ปัญหาการเรียกใช้ฟังก์ชันแบบคงที่หรือการผูกแบบคงที่ - การเรียกใช้ฟังก์ชันจะกระทำก่อนที่โปรแกรมจะถูกดำเนินการ บางครั้งเรียกว่าการผูกล่วงหน้าเนื่องจากมีการระบุฟังก์ชัน display() ในเวลาคอมไพล์โปรแกรม

ทีนี้มันจะเรียกใช้ฟังก์ชัน display ของคลาสที่ได้รับโดยไม่ต้องใช้คีย์เวิร์ดเสมือน (การเชื่อมล่าช้า) ก่อนฟังก์ชัน display ในคลาสฐานได้อย่างไร

ในโปรแกรมนี้ การส่งวัตถุเป็นการเรียกตามค่า การเรียกตามตัวชี้ และการโทรโดยอ้างอิงถึงฟังก์ชัน Hello จะทำงานได้ดี ตอนนี้ หากเราใช้ Polymorphism และต้องการแมปฟังก์ชันสมาชิกของคลาสที่ได้รับมาหากมันถูกเรียก เราจะต้องเพิ่มคีย์เวิร์ดเสมือนก่อนฟังก์ชัน map ในฐาน หากคุณส่งผ่านค่าของอ็อบเจ็กต์เมื่อเรียกใช้โดยใช้พอยน์เตอร์และการเรียกใช้โดยการอ้างอิง มันจะเป็นการเรียกใช้ฟังก์ชันในคลาสที่ได้รับ แต่ถ้าคุณส่งผ่านอ็อบเจ็กต์ตามค่า เหตุใดจึงไม่เป็นเช่นนั้น?>

คลาส A ( สาธารณะ: void display(); // virtual void display() ( cout<< "Hey from A" <display() ) int main() ( B obj; สวัสดี (obj); // obj //&ob กลับ 0; )

  • 2 คำตอบ
  • การเรียงลำดับ:

    กิจกรรม

4

ตอนนี้มันจะเรียกใช้ฟังก์ชัน display ของคลาสที่ได้รับโดยไม่ต้องใช้คีย์เวิร์ดเสมือน (การรวมล่าช้า) ก่อนฟังก์ชัน display ในคลาสฐานได้อย่างไร

ฟังก์ชันที่ไม่ใช่เสมือนได้รับการแก้ไขอย่างง่ายดายโดยคอมไพเลอร์ตามประเภทคงที่ของวัตถุ (หรือการอ้างอิงหรือตัวชี้) ที่เรียกใช้ ดังนั้นวัตถุที่กำหนดประเภทที่ได้รับเช่นเดียวกับการอ้างอิงถึงวัตถุย่อย:

BB; อ&a=b;

คุณจะได้ผลลัพธ์ที่แตกต่างจากการเรียกใช้ฟังก์ชันที่ไม่ใช่เสมือน:

B.display(); // เรียกว่า B a.display(); // เรียกว่า A

หากคุณรู้ประเภทจริงคุณสามารถระบุสิ่งที่คุณต้องการเรียกเวอร์ชันนี้ได้:

คงที่_คาสต์ (ก).จอแสดงผล(); // เรียกว่าบี

แต่สิ่งที่จะผิดมหันต์คือถ้าวัตถุที่ a อ้างถึงไม่มีประเภท B

ทีนี้ หากเราใช้ Polymorphism และต้องการแมปฟังก์ชันสมาชิกของคลาสที่ได้รับหากมันถูกเรียก เราจะต้องเพิ่มคีย์เวิร์ดเสมือนก่อนฟังก์ชัน map ในฐาน

เพื่อแก้ไข. หากคุณสร้างฟังก์ชันเสมือน ฟังก์ชั่นจะได้รับการแก้ไขขณะรันไทม์ตามประเภทไดนามิกของออบเจ็กต์ แม้ว่าคุณจะใช้ประเภทการอ้างอิงหรือตัวชี้อื่นในการเข้าถึงก็ตาม ทั้งสองตัวอย่างข้างต้นจะเรียกมันว่า B

ถ้าเราส่งผ่านค่าของวัตถุโดยการเรียกโดยตัวชี้และการโทรโดยการอ้างอิง มันจะเรียกใช้ฟังก์ชันในคลาสที่ได้รับ แต่ถ้าเราส่งผ่านวัตถุตามค่า นั่นไม่ได้หมายความว่าเหตุใดจึงเป็นเช่นนี้

หากคุณผ่านมันไปด้วยคุณค่าคุณก็ การหั่นมัน: คัดลอกเฉพาะส่วน A ของวัตถุเพื่อสร้างวัตถุใหม่ประเภท A ดังนั้น ไม่ว่าฟังก์ชันนี้จะเป็นเสมือนหรือไม่ก็ตาม การเรียกใช้ฟังก์ชันนี้บนวัตถุนี้จะเลือกเวอร์ชันของ A เนื่องจากเป็น A และไม่มีอะไรนอกจาก A

0

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

อัปเดตครั้งล่าสุด: 02/04/2019

ก่อนหน้านี้ เราพิจารณาสองวิธีในการเปลี่ยนแปลงการทำงานของวิธีการที่สืบทอดมาจากคลาสพื้นฐาน - การซ่อนและการแทนที่ ความแตกต่างระหว่างสองวิธีนี้คืออะไร?

แทนที่

ลองมาตัวอย่างที่มีวิธีการแทนที่:

บุคคลในคลาส ( สตริงสาธารณะ FirstName ( get; set; ) สตริงสาธารณะ LastName ( get; set; ) บุคคลสาธารณะ (string firstName, string LastName) ( FirstName = firstName; LastName = LastName; ) public virtual void Display() ( Console.WriteLine ($"(FirstName) (LastName)"); คลาส พนักงาน: บุคคล ( บริษัท สตริงสาธารณะ ( get; set; ) พนักงานสาธารณะ (ชื่อสตริง, นามสกุลสตริง, บริษัท สตริง) : ฐาน (ชื่อ, นามสกุล) ( บริษัท = บริษัท ; ) การแทนที่สาธารณะ void Display() ( Console.WriteLine($"(FirstName) (LastName) ทำงานใน (Company)"); ) )

เรามาสร้างวัตถุ Employee และส่งผ่านไปยังตัวแปรประเภท Person:

บุคคล tom = พนักงานใหม่ ("Tom", "Smith", "Microsoft"); ทอม.ดิสเพลย์(); // Tom Smith ทำงานที่ Microsoft

ตอนนี้เราได้ผลลัพธ์ที่แตกต่างจากการซ่อนตัว และเมื่อมีการเรียก tom.Display() การดำเนินการตามวิธี Display จากคลาส Employee จะถูกดำเนินการ

ในการทำงานกับเมธอดเสมือน คอมไพลเลอร์จะสร้างตารางเมธอดเสมือน (Virtual Method Table หรือ VMT) ที่อยู่ของวิธีการเสมือนถูกเขียนลงไป แต่ละชั้นเรียนมีตารางของตัวเอง

เมื่อคลาสอ็อบเจ็กต์ถูกสร้างขึ้น คอมไพลเลอร์จะส่งรหัสพิเศษไปยังตัวสร้างของอ็อบเจ็กต์ที่เชื่อมต่ออ็อบเจ็กต์และตาราง VMT

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

การปกปิด

ตอนนี้เรามาเรียนคลาสเดียวกัน Person และ Employee แต่แทนที่จะแทนที่เราใช้การซ่อน:

บุคคลในคลาส ( สตริงสาธารณะ ชื่อ ( get; set; ) สตริงสาธารณะ นามสกุล ( get; set; ) บุคคลสาธารณะ (สตริงชื่อ นามสกุลสตริง) ( FirstName = firstName; LastName = LastName; ) โมฆะสาธารณะ Display() ( Console.WriteLine( $"(FirstName) (LastName)" ) ) คลาสพนักงาน: บุคคล ( บริษัท สตริงสาธารณะ ( get; set; ) พนักงานสาธารณะ (ชื่อสตริง, นามสกุลสตริง, บริษัท สตริง) : ฐาน (ชื่อ, นามสกุล) ( บริษัท = บริษัท ; ) public new void Display() ( Console.WriteLine($"(FirstName) (LastName) ทำงานใน (Company)"); ) )

และมาดูกันว่าเกิดอะไรขึ้นในกรณีต่อไปนี้:

บุคคล tom = พนักงานใหม่ ("Tom", "Smith", "Microsoft"); ทอม.ดิสเพลย์(); //ทอม สมิธ

ตัวแปร Tom แสดงถึงประเภทบุคคล แต่เก็บการอ้างอิงไปยังอ็อบเจ็กต์ Employee อย่างไรก็ตาม เมื่อเรียกใช้เมธอด Display เวอร์ชันของเมธอดที่กำหนดไว้ในคลาส Person จะถูกดำเนินการ ไม่ใช่ในคลาส Employee ทำไม คลาส Employee ไม่ได้แทนที่วิธี Display ที่สืบทอดมาจากคลาสพื้นฐาน แต่กำหนดวิธีการใหม่จริง ๆ ดังนั้นเมื่อมีการเรียก tom.Display() วิธีการ Display จากคลาส Person จะถูกเรียก

---.NET Assemblies --- การผูกล่าช้า

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

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

คลาส System.Activator

ระบบชั้นเรียน Activator (กำหนดไว้ในแอสเซมบลี mscorlib.dll) มีบทบาทสำคัญในกระบวนการผูกข้อมูลล่าช้าใน .NET ในตัวอย่างปัจจุบัน สิ่งเดียวที่น่าสนใจในตอนนี้คือ วิธีการ Activator.CreateInstance()ซึ่งจะช่วยให้คุณสามารถสร้างอินสแตนซ์ของประเภทที่ถูกผูกไว้ล่าช้าได้ วิธีนี้มีโอเวอร์โหลดหลายครั้ง ดังนั้นจึงให้ความยืดหยุ่นค่อนข้างสูง ในเวอร์ชันที่ง่ายที่สุด CreateInstance() รับอ็อบเจ็กต์ Type ที่ถูกต้องซึ่งอธิบายเอนทิตีที่ควรจัดสรรให้กับหน่วยความจำได้ทันที

หากต้องการดูว่าสิ่งนี้หมายความว่าอย่างไร เรามาสร้างโปรเจ็กต์ใหม่ประเภท Console Application นำเข้าเนมสเปซ System.I0 และ System.Reflection ลงไปโดยใช้คีย์เวิร์ดที่ใช้ แล้วแก้ไขคลาส Program ดังที่แสดงด้านล่าง:

การใช้ระบบ; โดยใช้ System.Reflection; ใช้ System.IO; namespace ConsoleApplication1 ( คลาสโปรแกรม ( static void Main() ( Assembly ass = null; try ( ass = Assembly.Load("fontinfo"); ) catch (FileNotFoundException ex) ( Console.WriteLine(ex.Message); ) if (ass != null) CreateBinding(ass); Console.ReadLine(); ) static void CreateBinding(Assembly a) ( ลอง ( Type color1 = a.GetType("FontColor"); // ใช้วัตถุที่มีผลผูกพันล่าช้า obj = Activator.CreateInstance( color1); Console.WriteLine("วัตถุที่สร้างขึ้น!"); catch (ข้อยกเว้น เช่น) ( Console.WriteLine(ex.Message); ) )

ก่อนที่คุณจะสามารถเรียกใช้แอปพลิเคชันนี้ได้ คุณต้องคัดลอกแอสเซมบลี fontinfo.dll ไปยังไดเรกทอรีย่อย bin\Debug ภายในไดเรกทอรีของแอปพลิเคชันใหม่นี้ด้วยตนเองโดยใช้ Windows Explorer สิ่งนี้คือมีการเรียกเมธอด Assembly.Load() ที่นี่ ซึ่งหมายความว่า CLR จะตรวจสอบโฟลเดอร์ไคลเอนต์เท่านั้น (หากต้องการ คุณสามารถใช้เมธอด Assembly.LoadFrom() และระบุเส้นทางแบบเต็มไปยังแอสเซมบลี แต่ในกรณีนี้ก็ไม่จำเป็น)

ฟังก์ชั่นเสมือนจริง________________________________________________________________ 1

การผูกมัดช่วงต้นและปลาย พหุสัณฐานแบบไดนามิก ___________________________________ 1

ฟังก์ชันเสมือน____________________________________________________________________________ 1 ตัวทำลายเสมือน _______________________________________________________________ 4 คลาสนามธรรมและฟังก์ชันเสมือนล้วนๆ_______________________________________________ 5

ฟังก์ชั่นเสมือนจริง

การผูกมัดช่วงต้นและปลาย พหุสัณฐานแบบไดนามิก

C++ รองรับความหลากหลายในสองวิธี

ประการแรก ได้รับการรองรับในระหว่างการคอมไพล์ผ่านฟังก์ชันและการโอเวอร์โหลดของตัวดำเนินการ ความหลากหลายประเภทนี้เรียกว่า ความหลากหลายแบบคงที่เนื่องจากมีการใช้งานก่อนดำเนินการด้วยซ้ำ

โปรแกรมโดยการเชื่อมโยงเบื้องต้นของตัวระบุฟังก์ชันกับที่อยู่ทางกายภาพ ในขั้นตอนการรวบรวมและการเชื่อมโยง

ประการที่สอง รองรับระหว่างการทำงานของโปรแกรมผ่านฟังก์ชันเสมือน เมื่อพบการเรียกใช้ฟังก์ชันเสมือนในโค้ดโปรแกรม คอมไพเลอร์ (หรือที่เจาะจงกว่านั้นคือตัวเชื่อมโยง) จะกำหนดการเรียกนี้เท่านั้น โดยปล่อยให้การเชื่อมโยงของตัวระบุฟังก์ชันกับที่อยู่ของมันจนกระทั่งถึงขั้นตอนการดำเนินการ กระบวนการนี้เรียกว่า มีผลผูกพันล่าช้า.

ฟังก์ชันเสมือนคือฟังก์ชันที่มีการเรียก (และการดำเนินการที่ดำเนินการ) ขึ้นอยู่กับประเภทของออบเจ็กต์ที่ถูกเรียกใช้ วัตถุกำหนดว่าควรเรียกใช้ฟังก์ชันใดระหว่างการทำงานของโปรแกรม ความหลากหลายประเภทนี้เรียกว่า พหุสัณฐานแบบไดนามิก.

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

สำหรับข้อมูลโพลีมอร์ฟิกแต่ละชนิด คอมไพลเลอร์จะสร้างตารางของฟังก์ชันเสมือนและฝังตัวชี้ที่ซ่อนอยู่ลงในตารางนี้ในแต่ละออบเจ็กต์ของคลาสนี้ ประกอบด้วยที่อยู่ของฟังก์ชันเสมือนของออบเจ็กต์ที่เกี่ยวข้อง ชื่อของตัวชี้ไปยังตารางของฟังก์ชันเสมือนและชื่อของตารางขึ้นอยู่กับการใช้งานในคอมไพเลอร์เฉพาะ ตัวอย่างเช่น ใน Visual C++ 6.0 ตัวชี้นี้มีชื่อว่า vfptr และตารางเรียกว่า vftable (จากตารางฟังก์ชันเสมือนภาษาอังกฤษ) คอมไพลเลอร์จะฝังโค้ดไว้ที่จุดเริ่มต้นของตัวสร้างคลาส polymorphic โดยอัตโนมัติซึ่งจะเริ่มต้นตัวชี้ไปยังตารางของฟังก์ชันเสมือน หากเรียกใช้ฟังก์ชันเสมือน โค้ดที่สร้างโดยคอมไพเลอร์จะค้นหาตัวชี้ไปยังตารางฟังก์ชันเสมือน จากนั้นค้นหาตารางนั้นและดึงที่อยู่ของฟังก์ชันที่เกี่ยวข้องจากนั้น หลังจากนั้นจะมีการเปลี่ยนไปยังที่อยู่ที่ระบุและเรียกใช้ฟังก์ชัน

โปรดจำไว้ว่าเมื่อสร้างอ็อบเจ็กต์ของคลาสที่ได้รับ ตัวสร้างของคลาสฐานจะถูกเรียกก่อน ในขั้นตอนนี้ ตารางฟังก์ชันเสมือนจะถูกสร้างขึ้นพร้อมทั้งตัวชี้ไปยังตารางดังกล่าว หลังจากการเรียกตัวสร้างคลาสที่ได้รับ ตัวชี้ตารางฟังก์ชันเสมือนจะถูกตั้งค่าเพื่อให้อ้างอิงถึงฟังก์ชันเสมือนที่ถูกแทนที่ (ถ้ามี) ที่มีอยู่สำหรับวัตถุของคลาสนั้น

ในเรื่องนี้คุณต้องทราบราคาที่คุณต้องจ่ายสำหรับโอกาสในการใช้การผูกมัดล่าช้า

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

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

ฟังก์ชันที่รู้จักอินเทอร์เฟซการเรียก (เช่น ต้นแบบ) แต่ไม่สามารถระบุการใช้งานโดยทั่วไปได้และสามารถกำหนดได้เฉพาะบางกรณีเท่านั้น เรียกว่าเสมือน (คำที่หมายความว่าฟังก์ชันสามารถแทนที่ได้ในคลาสที่ได้รับ)

ฟังก์ชันเสมือนเป็นฟังก์ชันที่ทำให้แน่ใจว่ามีการเรียกใช้ฟังก์ชันที่ถูกต้องบนออบเจ็กต์ โดยไม่คำนึงว่าจะใช้นิพจน์ใดในการโทร

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

คลาสคอร์ด

คลาสพิกัดฐาน

// คลาสพิกัดฐาน

ได้รับการคุ้มครอง:

// สมาชิกชั้นเรียนที่ได้รับการคุ้มครอง

x สองเท่า, y;

//พิกัด

สาธารณะ:

// สมาชิกชั้นเรียนสาธารณะ

พิกัด() ( x = 0 ; y = 0 ; )

// ตัวสร้างคลาสพื้นฐาน

อินพุตเป็นโมฆะ();

// ประกาศฟังก์ชันที่ไม่ใช่เสมือน

โมฆะเสมือนพิมพ์ ();

// ประกาศฟังก์ชันเสมือน

เป็นโมฆะ Coord::อินพุต()

// อนุญาตให้คุณป้อนพิกัดจากแป้นพิมพ์

ศาล<<"\tx=";

// ป้อนค่า x จากคีย์บอร์ด

ศาล<<"\ty=";

// ป้อนค่า y จากคีย์บอร์ด

เป็นโมฆะ Coord::พิมพ์()

// แสดงค่าพิกัดบนหน้าจอ

ศาล<<"\tx="<

คลาสจุดที่ได้รับ

คลาสดอท: publicCoord

// ผู้สืบทอดคลาสพิกัด

ชื่อถ่าน ;

//ชื่อจุด

สาธารณะ:

// สมาชิกชั้นเรียนสาธารณะ

Dot (ch ar N) : Coord () ( name = N ; )

// เรียกตัวสร้างคลาสพื้นฐาน

อินพุตเป็นโมฆะ();

โมฆะพิมพ์();

โมฆะ Dot::Input()

// อนุญาตให้คุณป้อนพิกัดจุดจากแป้นพิมพ์

ถ่าน S ="ป้อนพิกัดของจุด";

ชาร์ทูเอม(S, S);

ศาล<

รหัส::อินพุต();

โมฆะ Dot::พิมพ์()

// แสดงค่าพิกัดของจุดบนหน้าจอ

ถ่าน S ="พิกัดจุด";

ชาร์ทูเอม(S, S);

// แปลงอักขระสตริงเป็น Cyrillic

ศาล<

// แสดงชื่อและชื่อของจุด

รหัส::พิมพ์();

// เรียกใช้ฟังก์ชันคลาสพื้นฐาน

คลาส Vec: publicCoord

คลาสเวกเตอร์ที่ได้รับ

// ผู้สืบทอดคลาสพิกัด

ชื่อถ่าน [ 3 ] ;

// ชื่อเวกเตอร์

สาธารณะ:

// สมาชิกชั้นเรียนสาธารณะ

Vec (ถ่าน * pName) : Coord () ( strncpy (name , pName , 3) ​​​​; name [ 2 ] = "\0" ; )

อินพุตเป็นโมฆะ();

// แทนที่ฟังก์ชันที่ไม่ใช่เสมือน

โมฆะพิมพ์();

// แทนที่ฟังก์ชันเสมือน

เป็นโมฆะ Vec::อินพุต()

// อนุญาตให้คุณป้อนการฉายภาพเวกเตอร์จากคีย์บอร์ด

การบรรยายครั้งที่ 9 ฟังก์ชั่นเสมือน 3

ถ่าน S ="ป้อนเส้นโครงของเวกเตอร์";// ประกาศและเริ่มต้นสตริงพร้อมท์

ชาร์ทูเอม(S, S);

// แปลงอักขระสตริงเป็น Cyrillic

ศาล<

// แสดงพรอมต์และชื่อเวกเตอร์

รหัส::อินพุต();

// เรียกใช้ฟังก์ชันคลาสพื้นฐาน

เป็นโมฆะ Vec::พิมพ์()

// แสดงค่าของการฉายภาพเวกเตอร์บนหน้าจอ

ถ่าน S = "เส้นโครงเวกเตอร์";

// ประกาศและเริ่มต้นบรรทัดส่วนหัว

ชาร์ทูเอม(S, S);

// แปลงอักขระสตริงเป็น Cyrillic

ศาล<

// แสดงชื่อและชื่อของเวกเตอร์

รหัส::พิมพ์();

// เรียกใช้ฟังก์ชันคลาสพื้นฐาน

ในตัวอย่างข้างต้น มีการประกาศคลาสพื้นฐาน Coord และคลาสที่ได้รับมาสองคลาส Dot และ Vec ฟังก์ชัน Print() ในคลาสที่ได้รับนั้นเป็นเสมือนเนื่องจากมีการประกาศเสมือนในคลาสพื้นฐาน Coord ฟังก์ชัน Print() ในคลาสที่ได้รับ Dot และ Vec จะแทนที่ฟังก์ชันคลาสพื้นฐาน หากคลาสที่ได้รับไม่ได้จัดเตรียมการใช้งานฟังก์ชัน Print() ที่ถูกแทนที่ การใช้งานเริ่มต้นจากคลาสพื้นฐานจะถูกใช้

ฟังก์ชัน Input() ได้รับการประกาศว่าไม่ใช่เสมือนในคลาสพื้นฐาน Coord และถูกแทนที่ในคลาสที่ได้รับ Dot และ Vec

เป็นโมฆะหลัก ()

Coord* pC = ใหม่ Coord();

// ประกาศตัวชี้เพื่อประสานงานและจัดสรรหน่วยความจำ

Dot* pD = จุดใหม่("D");

// ประกาศตัวชี้ไปยังจุดและจัดสรรหน่วยความจำ

Vec* pV = ใหม่ Vec("V");

// ประกาศตัวชี้ไปยังเวกเตอร์และจัดสรรหน่วยความจำ

พีซี -> อินพุต () ;

พีซี->พิมพ์ () ;

// เรียกใช้ฟังก์ชันเสมือน Coord::Print()

// ตัวชี้ไปยังพิกัดได้รับที่อยู่ของวัตถุประเภทจุด

พีซี -> อินพุต () ;

// เรียกใช้ฟังก์ชันที่ไม่ใช่เสมือน Coord::Input()

พีซี->พิมพ์ () ;

// เรียกใช้ฟังก์ชันเสมือน Dot::Print()

// ตัวชี้ไปยังพิกัดได้รับที่อยู่ของวัตถุประเภทเวกเตอร์

พีซี -> อินพุต () ;

// เรียกใช้ฟังก์ชันที่ไม่ใช่เสมือน Coord::Input()

พีซี->พิมพ์ () ;

// เรียกใช้ฟังก์ชันเสมือน Vec::Print()

ในตัวอย่างข้างต้น ตัวชี้พิกัด pC จะใช้ค่าที่อยู่ของวัตถุพิกัด จุด และเวกเตอร์สลับกัน แม้ว่าชนิดตัวชี้ pC จะไม่เปลี่ยนแปลง แต่จะเรียกใช้ฟังก์ชันเสมือนที่แตกต่างกัน ขึ้นอยู่กับค่าของมัน

เมื่อคุณใช้ตัวชี้คลาสพื้นฐานที่ชี้ไปยังวัตถุคลาสที่ได้รับจริง ๆ แล้ว จะมีการเรียกฟังก์ชันที่ไม่ใช่เสมือนของคลาสพื้นฐาน

ควรสังเกตว่าการดำเนินการกำหนด pC = pD ซึ่งใช้ตัวถูกดำเนินการประเภทต่างๆ (Coord* และ Dot*) โดยไม่มีการแปลง สามารถทำได้สำหรับตัวชี้คลาสพื้นฐานทางด้านซ้ายเท่านั้น การดำเนินการกำหนดย้อนกลับ pD = pC ไม่ถูกต้อง และทำให้เกิดข้อผิดพลาดทางไวยากรณ์

เมื่อดำเนินการ โปรแกรมจะแสดง:

พิกัดจุด D:

เส้นโครงของเวกเตอร์ V:

เมื่อเรียกใช้ฟังก์ชันโดยใช้พอยน์เตอร์และการอ้างอิง ให้ใช้กฎต่อไปนี้:

การเรียกใช้ฟังก์ชันเสมือนได้รับการแก้ไขตามประเภทของออบเจ็กต์ที่ที่อยู่ถูกจัดเก็บโดยตัวชี้หรือการอ้างอิง

การเรียกใช้ฟังก์ชันที่ไม่ใช่เสมือนได้รับการแก้ไขตามประเภทของตัวชี้หรือการอ้างอิง

ฟังก์ชันเสมือนจะถูกเรียกใช้เฉพาะบนอ็อบเจ็กต์ที่อยู่ในคลาสที่กำหนดเท่านั้น นั่นเป็นเหตุผล

คุณไม่สามารถประกาศฟังก์ชันเสมือนแบบโกลบอลหรือแบบคงที่ได้ คำหลักเสมือนสามารถ

  • เซอร์เกย์ ซาเวนคอฟ

    รีวิวแบบ "สั้นๆ" บ้าง... เหมือนรีบไปที่ไหนสักแห่ง