เกี่ยวกับการจัดสรรหน่วยความจำอ็อบเจ็กต์และการรีไซเคิล
ฉันไม่รู้ว่ามีใครมีความคิดเกี่ยวกับการวาง dl register ที่นี่หรือไม่ ถ้าไม่ลองคิดดูสิ ในความเป็นจริง ก่อนโค้ดต่อไปนี้ (1) มีโค้ดดังกล่าวอยู่บรรทัด MOV dl, 1 แล้วเหตุใดจึงเป็นเช่นนี้ เหตุผลไม่ง่ายนัก การนำโค้ดกลับมาใช้ใหม่ เกี่ยวกับการใช้ dl register ที่นี่ มีคำแนะนำในความช่วยเหลือของ Delphi จงอดทนและค้นหามันด้วยตัวเอง หากคุณพบมัน คุณจะเป็นผู้เชี่ยวชาญ นี่คือการเรียนรู้ ไม่ใช่ เรียนตกปลา ไม่ใช่ขอปลา บอร์แลนด์กล่าวไว้ว่า "ฉันใช้ dl register เพื่อเก็บแฟล็ก ถ้าค่าเป็น 1 ฉันจะสร้างอ็อบเจ็กต์ ไม่เช่นนั้น ฉันจะไม่สร้างอ็อบเจ็กต์นั้น" หากคุณมึนงงนิดหน่อย ลองคิดดูใหม่อีกครั้ง อีกครั้ง. หากคุณยังไม่ลืมตาลองดูโค้ดต่อไปนี้ (2) อย่าบอกว่าไม่พบ ไอ เกิดอะไรขึ้น ฉันพูดไร้สาระ ฉันสัญญาว่าจะไม่ พูดอีกครั้ง
{รหัส (1)
ทดสอบ ดล, ดล
เจซ +$08
เพิ่ม esp, -$10
โทร @ClassCreate }
{/// รหัส (2)
กระบวนการ Tapplication.CreateForm (InstanceClass: TComponentClass; var Reference);
var
อินสแตนซ์: TComponent;
เริ่ม
///////// อินสแตนซ์ := TComponent (InstanceClass.NewInstance);
TComponent (อ้างอิง) := อินสแตนซ์;
พยายาม
///////// อินสแตนซ์สร้าง (ตนเอง);
ยกเว้น
TComponent (อ้างอิง) := ไม่มี;
ยก;
จบ;
ถ้า (FMainForm = nil) และ (อินสแตนซ์คือ TForm) แล้ว
เริ่ม
TForm (อินสแตนซ์) .HandleNeeded;
FMainForm := TForm (อินสแตนซ์);
จบ;
จบ;
-
ในกรณีนี้ กล่าวคือ Delphi เรียกโค้ด (1) เมื่อมีการเรียกอินสแตนซ์ของคลาสที่สร้างขึ้นเป็นครั้งแรก จากนั้นถ้าใครต้องการเรียกใช้เมธอด Create ฉันไม่ใช่ มันเป็นคอมไพเลอร์เท่านั้น เช่น MOV dl นี้ 0 เพื่อที่คำสั่งกระโดดการตัดสิน 0 ในโค้ด (1) จะไปยังจุดที่ควรจะไป (ทำไมคุณถึงพูดเรื่องไร้สาระอีกแล้ว ครั้งสุดท้ายฉันสัญญา) อันที่จริงวิธีการสร้างที่นี่เป็นเพียง วิธีการทั่วไป และ คุณควรสังเกตด้วยว่าการเรียกเมธอด Create หรือเมธอด NewInstance ครั้งแรกคือคลาสที่ส่งข้อความนี้ (โปรดทราบว่าข้อความนี้ไม่ใช่ ข้อความ Windows ข้อความนี้ไม่ใช่ข้อความอื่น 555 มันไร้สาระอีกแล้ว เป็นครั้งสุดท้ายจริงๆ ถ้าไม่เข้าใจข้อความนี้ กรุณาศึกษาความรู้เรื่อง Object-Oriented และ Modeling ให้ละเอียด) แล้วโทรไป ในภายหลัง วิธีการสร้างเป็นวัตถุที่สร้างขึ้น Instance.Create(Self) ลองคิดดู แน่นอนว่าค่าของการลงทะเบียน dl เปลี่ยนไป
เอาล่ะ ฉันมีเวลาไม่มาก สุดท้ายนี้ฉันขอเสริมเรื่องธง (วิธีแปลของไต้หวัน ฉันคิดว่าคำนี้ดี ฉันขอแนะนำให้คุณใช้มันเพื่อช่วยฉันด้วย ปัญหาในอนาคตมากมาย) และคำแนะนำในการใช้ dl register นี้ ฉันควรจะบอกว่า จะไม่มีการเปลี่ยนแปลงใด ๆ หากคุณคิดให้ดี Delphi ไม่ได้เปลี่ยนจาก 1 เป็น 6 อย่างไรก็ตาม มันยังพิสูจน์ได้ว่านักออกแบบ HB ของ Delphi เป็นตัวละคร . แน่นอนฉันไม่สามารถเปรียบเทียบได้ ฮ่าๆ เอาอีกแล้ว โอเค ผมจะเลิกพูดแล้ว โอ้ ในที่สุด ฉันขอบอกว่าวิธี TObject.Create ไม่ใช่วิธีที่ว่างเปล่า จำไว้ แล้วลองคิดดู
โอเค เรื่องอื่นลองไปดูด้วยตนเองก็ได้ครับ
“ออกไปจากที่นี่ ฉันอยากเห็นและคิดเกี่ยวกับมัน แต่ฉันไม่ต้องการเรื่องไร้สาระของคุณ!” มีคนหัวเราะ! -
ลาก่อน
เมื่อคอมไพเลอร์จัดสรรหน่วยความจำให้กับอ็อบเจ็กต์ การสนับสนุนที่มีให้คือการแทรกบรรทัดโค้ดแอสเซมบลีเหล่านี้ก่อนที่จะเรียกตัวสร้าง:
ทดสอบ ดล, ดล
เจซ +$08
เพิ่ม esp, -$10
call @ClassCreate // ให้ความสนใจกับโค้ดบรรทัดนี้
บรรทัดสุดท้ายของโค้ดด้านบนเรียกฟังก์ชัน _ClassCreate ในบรรทัด 8949 ของไฟล์ system.pas (ขึ้นอยู่กับ Delphi 6) ฟังก์ชันนี้จัดสรรหน่วยความจำที่เหมาะสมสำหรับแต่ละวัตถุโดยเฉพาะ หลังจากการจัดสรรหน่วยความจำเสร็จสิ้น ตัวสร้างของคลาสจะถูกเรียกเพื่อเริ่มต้นสมาชิกข้อมูล หลังจากนั้นคอมไพเลอร์จะแทรกบรรทัดโค้ดแอสเซมบลีต่อไปนี้:
ทดสอบ ดล, ดล
เจซ +$0f
โทร @AfterConstruction
ป๊อป dWord ptr fs:[$00000000]
เพิ่ม esp, $0c
งานหลักคือการเรียก AfterConstruction ของแต่ละอินสแตนซ์การเรียกนี้ไม่มีประโยชน์ใน Delphi
ในทำนองเดียวกัน เมื่อทำลายวัตถุ จะต้องเรียกตัวทำลายล้างของคลาสก่อนเพื่อปล่อยทรัพยากรที่ร้องขอโดยวัตถุ หลังจากนั้นพื้นที่หน่วยความจำที่วัตถุครอบครองนั้นจะถูกรีไซเคิล งานนี้เสร็จสมบูรณ์โดยคอมไพเลอร์โดยแทรกโค้ดแอสเซมบลีต่อไปนี้หลังจากเรียก destructor:
โทรหา @BeforeDestruction
ทดสอบ ดล, ดล
เจแอล +$05
โทร @ClassDestroy
งานที่ทำโดยโค้ดเหล่านี้สอดคล้องกับสิ่งที่ทำเมื่อสร้างอ็อบเจ็กต์และการจัดสรรหน่วยความจำ โดยส่วนใหญ่จะเรียกฟังก์ชัน _ClassDestroy บนบรรทัด 8997 ใน system.pas
ตัวสร้างและตัวทำลาย
ในการกำหนดคอนสตรัคเตอร์ ให้ใช้คีย์เวิร์ด Constructor ตามแบบแผน ชื่อของคอนสตรัคเตอร์คือ Create (แน่นอนว่าสามารถใช้ชื่ออื่นได้ แต่นั่นไม่ใช่การออกแบบที่ดีเลย!) ชอบ:
พิมพ์
TMyFamily = class // คลาสที่กำหนดสำหรับครอบครัวของคุณ
ส่วนตัว
FMyFatherName : String; // ชื่อพ่อของคุณ
FMyMotherName : String; // ชื่อคุณแม่ของคุณ
… // สมาชิกคนอื่น ๆ ในครอบครัวของคุณ
สาธารณะ
ตัวสร้างสร้าง (strFatherName, strMotherName : String);
…… // วิธีการอื่นๆ
จบ;
คุณอาจถามว่าถ้าฉันไม่ได้จัดเตรียม Constructor สำหรับคลาสของฉัน จะสร้างอ็อบเจ็กต์ของมันได้หรือไม่ คำตอบคือ: ใช่ มีการกล่าวถึงเหตุผลก่อนหน้านี้ การจัดสรรหน่วยความจำที่วัตถุครอบครองนั้นเสร็จสมบูรณ์โดยคอมไพเลอร์ และเนื่องจากใน Object Pascal คลาสทั้งหมด (ยกเว้นคลาส TObject เอง) ได้มาจากคลาส TObject คอมไพเลอร์จะเรียกตัวสร้าง TObject.Create() แต่ฟังก์ชันนี้เป็นฟังก์ชันว่าง และจะไม่ส่งผลกระทบต่อคลาส TMyFamily เมื่อสมาชิกข้อมูล (FMyFatherName, FMyMotherName) ถูกเตรียมใช้งาน พวกเขาจะถูกล้างเป็นสตริงว่างโดยอัตโนมัติ (เช่น '') เนื่องจาก TObject.Create() ไม่รู้จักพ่อหรือแม่ของคุณเลย!
เมื่อสร้างวัตถุ ตัวสร้างจะถูกเรียกโดยตรง ในรูปแบบต่อไปนี้:
MyFamilyObject := TMyFamily.Create('จาง', 'Li');
หากต้องการกำหนด destructor ให้ใช้คีย์เวิร์ด Destructor ตามแบบแผน destructor จะมีชื่อว่า Destroy ชอบ:
พิมพ์
TMyClass = คลาส
สาธารณะ
ทำลายล้าง();
จบ;
เหตุผลที่เพิ่มคำสั่งแทนที่ในตอนท้ายของการประกาศ destructor คือเพื่อให้แน่ใจว่าวัตถุสามารถทำลายได้อย่างถูกต้องในกรณีของความหลากหลาย (polymorphism จะกล่าวถึงรายละเอียดในหัวข้อ 2.4) หากคุณไม่เพิ่มคีย์เวิร์ดแทนที่ คอมไพเลอร์จะให้คำเตือนคล้ายกับ "วิธีการ 'ทำลาย' ซ่อนวิธีการเสมือนประเภทพื้นฐาน 'TObject'" คำเตือนหมายความว่า Destroy ที่คุณกำหนดจะซ่อนเมธอดเสมือน TObject.Destroy() ของคลาสพื้นฐาน ในกรณีนั้น วัตถุจะไม่สามารถทำลายได้อย่างถูกต้องในสถานการณ์ polymorphic
หมายเหตุ: จำเป็นต้องประกาศตัวทำลายด้วยคำสั่งแทนที่
ในทำนองเดียวกัน หากไม่มีทรัพยากรพิเศษที่จำเป็นต้องเผยแพร่ในชั้นเรียนของคุณ คุณไม่จำเป็นต้องกำหนด destructor อย่างไรก็ตาม เมื่อทำลายวัตถุ คุณควรเรียกใช้เมธอด Free() ของวัตถุ แทนที่จะเรียก Destroy() โดยตรง
MyFamilyObject.Free();
เนื่องจากเมธอด Free() จะกำหนดว่าอ็อบเจ็กต์นั้นเป็นศูนย์หรือไม่ และหากไม่ใช่ศูนย์ ก็จะเรียก Destroy() ของอ็อบเจ็กต์เพื่อเพิ่มความปลอดภัย ขณะนี้มีวิธีที่ปลอดภัยกว่าในการทำเช่นนี้ ก็ไม่มีเหตุผลที่จะไม่ทำเช่นนั้นอย่างแน่นอน
หมายเหตุ: ห้ามเรียก Destroy() บนอ็อบเจ็กต์โดยตรง แต่ให้เรียก Free() แทน
สรุปได้ว่าใน Object Pascal คุณเพียงแค่ต้องใส่ใจกับการจัดสรรและปล่อยทรัพยากรที่วัตถุใช้ และไม่จำเป็นต้องสนใจพื้นที่ที่วัตถุครอบครอง!