โยน std ใหม่ :: ข้อยกเว้นเทียบกับการโยน std :: ข้อยกเว้น


113

ในขณะที่ดูรหัสบางอย่างฉันสะดุดเข้า:

throw /*-->*/new std::exception ("//...

และฉันคิดเสมอว่าคุณไม่ต้องการ / คุณไม่ควรใช้newที่นี่
วิธีที่ถูกต้องคืออะไรตกลงทั้งคู่ถ้าเป็นเช่นนั้นมีความแตกต่างกันอย่างไร

BTW จากสิ่งที่ฉันสามารถมองเห็นขณะที่ "grepping" กับ PowerShell เพิ่ม libs throw newไม่เคยใช้

PS ยังผมพบว่าบางรหัส CLI throw gcnewที่ใช้ ไม่เป็นไร?


1
ฉันคิดว่าthrow gcnewจะมีประโยชน์เช่น หากคุณต้องการให้โค้ดที่มีการจัดการตรวจจับข้อยกเว้นของคุณ ใครช่วยแก้ไขให้ฉันที
jpalecek

1
.Net จัดการกับข้อยกเว้นตามตัวชี้ดังนั้นการโยน gcnew จึงเป็นสิ่งที่ถูกต้อง
Sebastian Redl

1
@SebastianRedl .Net "ตัวชี้" อาจจะคลุมเครือ? แม้ว่า gcnew จะไม่แน่นอน System::Exceptionโดยทั่วไปจะอ้างอิงถึงวัตถุที่มีการจัดการบนกองขยะที่รวบรวม ฉันได้เสมอกับโยนและจับกับgcnew System::Exception ^แน่นอนฉันใช้finallyตลอดเวลาใน C ++ / CLI เช่นกันแม้ว่าจะไม่ได้ผสมกับข้อยกเว้น C ++ ในtryบล็อกเดียวกันบ่อยครั้งแต่ฉันไม่แน่ใจว่าทำไม

คำตอบ:


89

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

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

หากคุณโยนตัวชี้ไปยังวัตถุที่จัดสรรแบบไดนามิกคุณต้องแน่ใจว่าสิ่งที่ call stack มีลักษณะเป็นอย่างไร ณ จุดที่คุณต้องการทิ้งข้อยกเว้นของคุณจะมี catch block ที่ตั้งชื่อประเภทตัวชี้ที่ถูกต้องและมีการdeleteเรียกที่เหมาะสม ข้อยกเว้นของคุณจะต้องไม่ถูกจับได้catch (...)เว้นแต่ว่าบล็อกนั้นจะโยนข้อยกเว้นอีกครั้งซึ่งจะถูกจับโดยบล็อกอื่นที่จัดการกับข้อยกเว้นได้อย่างถูกต้อง

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


1
"โยนวัตถุยกเว้น" กองหรือกองเพื่อนของฉัน? กองหรือกอง? (บางทีฉันอาจกำลังดูตัวอย่างทั่วโลกที่ไม่ดีอยู่ที่ไหนสักแห่ง) โอ้แล้วถ้า stack ขอบเขตที่เหมาะสมคืออะไร?

@ebyrob: ฉันไม่ได้จริงๆว่าสิ่งที่คุณกำลังถามเกี่ยวกับ แต่ดูเหมือนคุณอยากรู้เกี่ยวกับการจัดเก็บและ / หรืออายุการใช้งานของวัตถุข้อยกเว้นที่อาจจะตอบได้ที่นี่ ถ้าไม่คุณอาจถามคำถามแยกกันดีกว่า
CB Bailey

31

ไม่จำเป็นต้องใช้newเมื่อโยนข้อยกเว้น

แค่เขียน:

throw yourexception(yourmessage);

และจับเป็น:

catch(yourexception const & e)
{
      //your code (probably logging related code)
}

โปรดทราบว่าyourexceptionควรได้มาจากstd::exceptionทางตรงหรือทางอ้อม


7
ทำไม? ทำไมไม่ใช้new? มาyourexceptionจากเหตุstd::exceptionใด
Walter

เมื่อฉันขี้เกียจ (ซึ่งมักจะรอ) ทำไมไม่throw std::exception;ทำงาน? g ++ ดูเหมือนจะไม่คอมไพล์เลย ...

7
@ebyrob: std::exceptionเป็นชนิดและคุณไม่สามารถโยนประเภทที่คุณได้ไปโยนวัตถุ ดังนั้นไวยากรณ์ควรเป็นดังนี้throw std::exception();ที่จะรวบรวม ตอนนี้ดีแค่ไหนเป็นคำถามที่แตกต่างกันโดยสิ้นเชิง
Nawaz

22

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


27
การขว้างnew std::exceptionจะถูกต้องก็ต่อเมื่อไซต์การโทรคาดว่าจะจับตัวชี้ได้และคาดว่าจะเข้าควบคุมการจัดการข้อยกเว้นการจัดสรรและจะไม่มีกรณีใด ๆ ที่ฟังก์ชันของคุณจะถูกเรียกโดยสิ่งที่ไม่ได้จับอย่างชัดเจน ตัวชี้ที่ถูกต้อง ( catch(...)หรือไม่มีการจัดการเลย) มิฉะนั้นจะมีวัตถุรั่วไหล ในระยะสั้นสามารถประมาณได้ว่า "ไม่เคย"
CB Bailey

อยากรู้ว่าคำตอบนี้ได้รับการยอมรับอย่างไรในเมื่อความคิดเห็นของ @ CharlesBailey เป็นคำตอบที่ถูกต้อง
John Dibling

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

ชาร์ลส์ไม่ได้ให้คำตอบของเขาและ A นี้ (ไม่เหมือนอีกข้อ) มีคำอธิบายทั้งใน A และความคิดเห็น
NoSenseEtAl

9

คำถามที่พบบ่อยเกี่ยวกับ C ++ มีการอภิปรายที่ดีเกี่ยวกับเรื่องนี้:

  1. https://isocpp.org/wiki/faq/exceptions#what-to-catch
  2. https://isocpp.org/wiki/faq/exceptions#catch-by-ptr-in-mfc

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


2
ตามปกติคำถามที่พบบ่อยจะใช้คำไม่ดี คุณสามารถจับตามค่าหรืออ้างอิง ตัวชี้เกิดขึ้นเพื่อเป็นค่า (ที่คุณจับตามค่าหรือการอ้างอิง) จำประเภทAแตกต่างจากประเภทA*ดังนั้นถ้าฉันไม่throw A()สามารถจับได้catch(A* e)เนื่องจากเป็นประเภทที่แตกต่างกันอย่างสิ้นเชิง
Martin York

ลิงก์เหล่านี้ใช้งานไม่ได้แล้ว
stephenspann

1
ฉันแก้ไขลิงค์ @spanndemic
user1202136

1

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

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.