เสร็จสิ้นบทที่ 3 ข้อยกเว้นและการจัดการข้อผิดพลาด ข้อความที่ตัดตอนมา
ตัวสร้างและข้อยกเว้น
หัวข้อนี้มักถูกกล่าวถึงในชุมชน C++ แต่ดูเหมือนว่าจะไม่มีใครสนใจหัวข้อนี้ในชุมชน Delphi อาจเนื่องมาจากลักษณะของภาษาโปรแกรมเมอร์ Delphi จึงไม่ต้องกังวลกับปัญหานี้ แต่ฉันคิดว่าโปรแกรมเมอร์ Delphi ควรเข้าใจปัญหานี้ด้วยและรู้ว่าภาษานี้ให้อะไรแก่เราเพื่อให้เราเพิกเฉยต่อมันได้ง่ายมาก ดังสุภาษิตที่ว่า “เมื่อถูกรายล้อมไปด้วยพร ก็ต้องรู้จักพร” เรารู้ว่า Constructor ของคลาสไม่มีค่าที่ส่งคืน หาก Constructor ล้มเหลวในการสร้างอ็อบเจ็กต์ ก็เป็นไปไม่ได้ที่จะพึ่งพาการส่งคืนรหัสข้อผิดพลาด ดังนั้นจะระบุความล้มเหลวของตัวสร้างในโปรแกรมได้อย่างไร? วิธีการ "มาตรฐาน" ที่สุดคือ: ส่งข้อยกเว้น ความล้มเหลวของ Constructor หมายความว่าการก่อสร้างวัตถุล้มเหลว ดังนั้นหลังจากมีข้อยกเว้นเกิดขึ้น วัตถุ "half-dead" นี้จะถูกจัดการอย่างไร? ในที่นี้ ฉันคิดว่าจำเป็นต้องมีความเข้าใจว่า C++ จัดการกับสถานการณ์นี้อย่างไรก่อนที่จะอ่าน ใน C ++ ตัวทำลายจะไม่ถูกเรียกหลังจากที่ตัวสร้างส่งข้อยกเว้น แนวทางนี้มีความสมเหตุสมผลเนื่องจากวัตถุดังกล่าวยังสร้างไม่เสร็จสมบูรณ์ในขณะนี้ หาก Constructor ดำเนินการบางอย่าง เช่น การจัดสรรหน่วยความจำ การเปิดไฟล์ ฯลฯ คลาส C++ จำเป็นต้องมีสมาชิกของตัวเองเพื่อจดจำการดำเนินการที่ได้ดำเนินการไป แน่นอนว่านี่เป็นเรื่องที่ยุ่งยากมากสำหรับผู้ใช้คลาส ดังนั้นโดยทั่วไปผู้ใช้งานคลาส C++ จะหลีกเลี่ยงการโยนข้อยกเว้นในตัวสร้าง (คุณสามารถจัดเตรียมฟังก์ชันสมาชิก เช่น Init และ UnInit ซึ่งปล่อยให้เป็นคอนสตรัคเตอร์หรือไคลเอ็นต์คลาส) ความล้มเหลวในการเริ่มต้น) วิธีแก้ปัญหาในหนังสือ C++ คลาสสิกทุกเล่มคือการใช้พอยน์เตอร์อัจฉริยะ (auto_ptr คลาสมาตรฐานของ STL) ใน Object Pascal ปัญหานี้ง่ายมาก และโปรแกรมเมอร์ก็ไม่ต้องกังวลกับมัน หากคลาส Object Pascal ส่งข้อยกเว้นในตัวสร้าง คอมไพลเลอร์จะเรียกตัวทำลายของคลาสโดยอัตโนมัติ (เนื่องจากตัวทำลายไม่ได้รับอนุญาตให้โอเวอร์โหลด จึงรับประกันได้ว่าจะมีตัวทำลายเพียงตัวเดียว ดังนั้นคอมไพเลอร์จะไม่สับสน . ท่ามกลางผู้ทำลายล้างหลายราย) โดยทั่วไปอ็อบเจ็กต์สมาชิกจะถูกทำลายใน destructor และเมธอด Free() ช่วยให้แน่ใจว่า destructor จะไม่ถูกเรียกใช้บนอ็อบเจ็กต์ศูนย์ (นั่นคือ อ็อบเจ็กต์สมาชิกที่ยังไม่ได้ถูกสร้างขึ้น) ดังนั้น ในขณะที่ทำให้โค้ดกระชับและสวยงาม แต่ยังรับประกันความปลอดภัยอีกด้วย พิมพ์ MyClass = classPRivateFStr : PChar; // String pointer publicconstructor Create();destructor Destroy(); override;end;constructor MyClass.Create();beginFStr := StrAlloc(10); // String pointer ใน Constructor จัดสรรหน่วยความจำ StrCopy (FStr, 'ABCDEFGHI'); เพิ่มข้อยกเว้นสร้าง ('ข้อผิดพลาด'); โยนข้อยกเว้นไม่มีเหตุผล ฮ่าฮ่าend;destructor A.Destroy();beginStrDispose(FStr); // ปล่อยหน่วยความจำใน destructor WriteLn('Free Resource');end;varObj : TMyClass;i : integer;begintryObj : = TMyClass.Create ();Obj.Free();WriteLn('สำเร็จ');ยกเว้นObj := nil;WriteLn('Failed');end;Read(i); // หยุดหน้าจอชั่วคราวเพื่อดูผลลัพธ์ที่กำลังทำงานอยู่ ในโค้ดนี้ ตัวสร้างจะส่งข้อยกเว้น และผลการดำเนินการคือ: Free ResourceFailed ในขณะนี้" เอาต์พุต Free Resource" ถูกสร้างขึ้นโดยคอมไพเลอร์ที่เรียก destructor โดยอัตโนมัติ ดังนั้น ถ้าเอกสารประกอบของคลาสหรือผู้เขียนคลาสบอกคุณว่าตัวสร้างคลาสอาจมีข้อยกเว้น ดังนั้นอย่าลืมห่อมันด้วย try...ยกเว้น! วิธีต่างๆ ที่ C++ และ Object Pascal จัดการกับข้อยกเว้นที่เกิดขึ้นโดยตัวสร้าง จริงๆ แล้วถือเป็นศูนย์รวมของแนวคิดการออกแบบของทั้งสองภาษา C++ ยึดถือสไตล์ของ C และมุ่งเน้นไปที่ประสิทธิภาพ ทุกอย่างตกเป็นหน้าที่ของโปรแกรมเมอร์ และคอมไพเลอร์ไม่ได้ดำเนินการใดๆ ที่ไม่จำเป็น Object Pascal สืบทอดสไตล์ของ Pascal และมุ่งเน้นไปที่ความหมายที่สวยงามของโปรแกรม คอมไพเลอร์ช่วยให้โปรแกรมเมอร์ทำงานที่ซับซ้อนได้