ทำไมไม่มีการสร้าง 'สุดท้าย' ใน C ++


57

การจัดการข้อยกเว้นใน C ++ นั้น จำกัด ให้ลอง / โยน / จับ ไม่เหมือนกับ Object Pascal, Java, C # และ Python แม้ใน C ++ 11 finallyโครงสร้างจะไม่ถูกนำมาใช้

ฉันได้เห็นวรรณกรรม C ++ จำนวนมากที่พูดถึง "ข้อยกเว้นรหัสปลอดภัย" Lippman เขียนว่ารหัสความปลอดภัยยกเว้นเป็นหัวข้อที่มีความสำคัญ แต่ขั้นสูงและยากเกินกว่าขอบเขตของไพรเมอร์ของเขา - ซึ่งดูเหมือนว่าบอกเป็นนัยว่ารหัสความปลอดภัยไม่ได้เป็นพื้นฐานของ C ++ Herb Sutter อุทิศ 10 บทในหัวข้อในภาษา C ++ อันยอดเยี่ยมของเขา!

สำหรับฉันแล้วดูเหมือนว่าปัญหาต่าง ๆ ที่เกิดขึ้นเมื่อพยายามเขียน "รหัสที่ปลอดภัยยกเว้น" สามารถแก้ไขได้ค่อนข้างดีหากมีการใช้finallyงานโครงสร้างซึ่งทำให้โปรแกรมเมอร์สามารถมั่นใจได้ว่าแม้ในกรณีที่มีข้อยกเว้นโปรแกรมสามารถคืนค่าได้ ไปยังสถานะที่ปลอดภัยมั่นคงปราศจากการรั่วไหลใกล้กับจุดจัดสรรทรัพยากรและรหัสที่อาจเป็นปัญหา ในฐานะที่เป็นโปรแกรมเมอร์ Delphi และ C # ที่มีประสบการณ์มากฉันใช้ลอง .. ในที่สุดบล็อกค่อนข้างครอบคลุมในรหัสของฉันเช่นเดียวกับโปรแกรมเมอร์ส่วนใหญ่ในภาษาเหล่านี้

เมื่อพิจารณาถึง 'ระฆังและเสียงนกหวีด' ทั้งหมดที่ใช้ใน C ++ 11 ฉันประหลาดใจที่พบว่า 'ในที่สุด' ก็ยังไม่อยู่ที่นั่น

ดังนั้นทำไมfinallyโครงสร้างไม่เคยถูกนำมาใช้ใน C ++ ไม่ใช่เรื่องยากหรือแนวคิดขั้นสูงที่จะเข้าใจและไปไกลเพื่อช่วยโปรแกรมเมอร์เขียน 'รหัสที่ปลอดภัยยกเว้น'


25
ทำไมไม่มีในที่สุด? เพราะคุณปล่อยสิ่งต่าง ๆ ใน destructor ที่ไฟโดยอัตโนมัติเมื่อวัตถุ (หรือตัวชี้สมาร์ท) ออกจากขอบเขต Destructors นั้นเหนือกว่า {} ในที่สุดเนื่องจากแยกเวิร์กโฟลว์ออกจากตรรกะการล้างข้อมูล เช่นเดียวกับที่คุณไม่ต้องการให้โทรฟรี () ทำให้เวิร์กโฟลว์ของคุณยุ่งเหยิงในภาษาที่รวบรวมขยะ
mike30


8
ถามคำถาม "ทำไมไม่มีfinallyใน C ++ และมีเทคนิคใดบ้างที่ใช้ในการจัดการข้อยกเว้นในสถานที่นั้น" ถูกต้องและในหัวข้อสำหรับเว็บไซต์นี้ ฉันคิดว่าคำตอบที่มีอยู่ครอบคลุมเรื่องนี้เป็นอย่างดี เปลี่ยนเป็นการอภิปรายเกี่ยวกับ "เหตุผลของนักออกแบบ C ++ ที่ไม่finallyคุ้มค่าหรือไม่" และ "ควรfinallyเพิ่มใน C ++ หรือไม่" และดำเนินการอภิปรายเกี่ยวกับความคิดเห็นเกี่ยวกับคำถามและคำตอบทุกข้อไม่ตรงกับรูปแบบของเว็บไซต์ถามตอบ
Josh Kelley

2
หากสุดท้ายคุณมีข้อกังวลอยู่แล้ว: บล็อคโค้ดหลักอยู่ที่นี่และข้อกังวลในการล้างข้อมูลจะได้รับการดูแลที่นี่
Kaz

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

คำตอบ:


56

ในฐานะที่เป็นความเห็นเพิ่มเติมเกี่ยวกับคำตอบของ @ Nemanja (ซึ่งเนื่องจากคำพูดของ Stroustrup นั้นเป็นคำตอบที่ดีพอ ๆ กับที่คุณจะได้รับ)

มันเป็นเพียงเรื่องของการทำความเข้าใจปรัชญาและสำนวนของ C ++ ใช้ตัวอย่างของการดำเนินการที่เปิดการเชื่อมต่อฐานข้อมูลในคลาสถาวรและต้องตรวจสอบให้แน่ใจว่าได้ปิดการเชื่อมต่อนั้นหากมีข้อยกเว้นเกิดขึ้น นี่เป็นเรื่องของความปลอดภัยยกเว้นและใช้กับภาษาใด ๆ ที่มีข้อยกเว้น (C ++, C #, Delphi ... )

ในภาษาที่ใช้try/ finallyรหัสอาจมีลักษณะดังนี้:

database.Open();
try {
    database.DoRiskyOperation();
} finally {
    database.Close();
}

เรียบง่ายและตรงไปตรงมา อย่างไรก็ตามมีข้อเสียบางประการ:

  • หากภาษานั้นไม่มีตัวทำลายล้างแบบกำหนดแน่นอนฉันต้องเขียนfinallyบล็อกเสมอมิฉะนั้นฉันจะรั่วไหลทรัพยากร
  • ถ้าDoRiskyOperationเป็นมากกว่าการเรียกเมธอดเดียว - ถ้าฉันมีการประมวลผลที่ต้องทำในtryบล็อก - จากนั้นการCloseดำเนินการอาจสิ้นสุดลงเล็กน้อยจากการOpenดำเนินการ ฉันไม่สามารถเขียนการล้างข้อมูลของฉันถัดจากการได้มา
  • หากฉันมีทรัพยากรหลายอย่างที่จำเป็นต้องได้รับจากนั้นปลดปล่อยในลักษณะที่ปลอดภัยยกเว้นฉันสามารถจบลงด้วยtry/ finallyบล็อกหลายชั้นลึก

วิธี C ++ จะมีลักษณะเช่นนี้:

ScopedDatabaseConnection scoped_connection(database);
database.DoRiskyOperation();

วิธีนี้จะช่วยแก้ไขข้อเสียทั้งหมดของfinallyวิธีการนี้อย่างสมบูรณ์ มันมีข้อเสียของตัวเองสองสามข้อ แต่ค่อนข้างเล็ก:

  • มีโอกาสที่ดีที่คุณจะต้องเขียนScopedDatabaseConnectionชั้นเรียนด้วยตัวเอง อย่างไรก็ตามมันเป็นการใช้งานที่ง่ายมาก - มีโค้ดเพียง 4 หรือ 5 บรรทัด
  • มันเกี่ยวข้องกับการสร้างตัวแปรท้องถิ่นเพิ่มเติม - ซึ่งคุณไม่ได้เป็นแฟนของความเห็นของคุณเกี่ยวกับ "การสร้างและทำลายคลาสอย่างต่อเนื่องเพื่อพึ่งพา destructors ของพวกเขาในการทำความสะอาดระเบียบของคุณแย่มาก" - แต่คอมไพเลอร์ที่ดีจะปรับให้เหมาะสม หางานพิเศษใด ๆ ที่ตัวแปรเฉพาะที่เกี่ยวข้อง การออกแบบ C ++ ที่ดีนั้นอาศัยการเพิ่มประสิทธิภาพหลายประเภท

โดยส่วนตัวเมื่อพิจารณาถึงข้อดีและข้อเสียเหล่านี้ฉันพบว่า RAII เป็นเทคนิคที่เป็นที่นิยมfinallyมากกว่า ไมล์สะสมของคุณอาจแตกต่างกันไป

ท้ายที่สุดเพราะ RAII เป็นสำนวนที่ได้รับการยอมรับเป็นอย่างดีในภาษาซีพลัสพลัสและเพื่อลดภาระของนักพัฒนาในการเขียนScoped...คลาสจำนวนมากจึงมีห้องสมุดเช่นScopeGuardและBoost.ScopeExitที่อำนวยความสะดวกในการล้างข้อมูลแบบกำหนด


8
C # มีusingคำสั่งซึ่งจะล้างข้อมูลวัตถุใด ๆ ที่ใช้IDisposableอินเทอร์เฟซโดยอัตโนมัติ ดังนั้นในขณะที่เป็นไปได้ที่จะเข้าใจผิดมันค่อนข้างง่ายที่จะทำให้ถูกต้อง
Robert Harvey

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

15
@MasonWheeler - อืมฉันไม่ได้บอกว่าการเขียนคลาสใหม่จะได้รับการพิจารณาเป็นพิเศษ ฉันบอกว่ามันเป็นข้อเสีย เกี่ยวกับความสมดุลแม้ว่าฉันชอบ RAII ที่จะมีfinallyการใช้ เช่นที่ฉันพูดไมล์สะสมของคุณอาจแตกต่างกันไป
Josh Kelley

7
@JoshKelley: 'การออกแบบ C ++ ที่ดีต้องอาศัยการเพิ่มประสิทธิภาพเหล่านี้เป็นอย่างมาก' การเขียน gobs ของรหัสที่ไม่เกี่ยวข้องแล้วอาศัยการเพิ่มประสิทธิภาพของคอมไพเลอร์คือการออกแบบที่ดี ! IMO เป็นสิ่งที่ตรงกันข้ามกับการออกแบบที่ดี ในบรรดาพื้นฐานของการออกแบบที่ดีคือรัดกุมรหัสที่อ่านง่าย น้อยกว่าในการดีบัก, น้อยกว่าในการรักษา ฯลฯ ฯลฯ คุณไม่ควรเขียน gobs ของรหัสและพึ่งพาคอมไพเลอร์เพื่อทำให้มันหมดไป - IMO ที่ไม่สมเหตุสมผลเลย!
เวกเตอร์

14
@ Mikey: ดังนั้นการทำสำเนารหัสการล้างข้อมูล (หรือความจริงที่ว่าการล้างข้อมูลต้องเกิดขึ้น) ทั่วรหัสนั้นคือ "รัดกุม" และ "ง่ายต่อการอ่าน"? ด้วย RAII คุณเขียนรหัสดังกล่าวเพียงครั้งเดียวและจะนำไปใช้โดยอัตโนมัติทุกที่
Mankarse

54

จากเหตุใด C ++ จึงไม่สร้างโครงสร้าง "ในที่สุด" ในC ++ สไตล์และเทคนิคของ Bjarne Stroustrup คำถามที่พบบ่อย :

เนื่องจาก C ++ สนับสนุนทางเลือกที่เกือบจะดีกว่าเสมอ: เทคนิค "การได้มาซึ่งทรัพยากรเป็นการเริ่มต้น" (TC ++ PL3 ส่วน 14.4) แนวคิดพื้นฐานคือการแสดงทรัพยากรโดยวัตถุในท้องถิ่นเพื่อให้ตัวทำลายวัตถุในท้องถิ่นจะปล่อยทรัพยากร ด้วยวิธีนี้โปรแกรมเมอร์ไม่สามารถลืมที่จะปล่อยทรัพยากร


5
แต่ไม่มีอะไรเกี่ยวกับเทคนิคที่เฉพาะเจาะจงกับ C ++ ใช่ไหม คุณสามารถทำ RAII ในภาษาใด ๆ กับวัตถุตัวสร้างและ destructors มันเป็นเทคนิคที่ยอดเยี่ยม แต่ RAII ที่มีอยู่เดิมไม่ได้หมายความว่าfinallyโครงสร้างจะไร้ประโยชน์ตลอดไปและตลอดไปแม้ว่า Strousup จะพูดอะไรก็ตาม ข้อเท็จจริงที่ว่าการเขียน "รหัสปลอดภัยยกเว้น" เป็นเรื่องใหญ่ใน C ++ เป็นข้อพิสูจน์เรื่องนี้ Heck, C # มีทั้ง destructors และfinallyและทั้งคู่ต่างก็คุ้นเคย
Tacroy

28
@Tacroy: C ++ เป็นหนึ่งในภาษาหลักน้อยมากที่มีกำหนด destructors C # "destructors" ไม่มีประโยชน์สำหรับจุดประสงค์นี้และคุณต้องเขียนบล็อก "using" ด้วยตนเองเพื่อให้มี RAII
Nemanja Trifunovic

15
@ Mikey คุณมีคำตอบของ "ทำไม C ++ ไม่ให้โครงสร้าง" ในที่สุด "? โดยตรงจาก Stroustrup ตัวเองที่นั่น สิ่งเพิ่มเติมที่คุณสามารถขอ? นั่นคือเหตุผล

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

19
@Kaz: ฉันต้องจำไว้ว่าให้ทำการล้างข้อมูลใน destructor เพียงครั้งเดียวและหลังจากนั้นฉันก็ใช้วัตถุ ฉันต้องจำไว้ว่าให้ทำการล้างข้อมูลในบล็อคสุดท้ายทุกครั้งที่ฉันใช้การดำเนินการที่จัดสรรไว้
deworde

19

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

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

เหตุผลที่กล่าวถึงความปลอดภัยของข้อยกเว้นเพิ่มเติมในชุมชน C ++ อาจเกิดจากข้อเท็จจริงที่ว่าใน C ++ คุณต้องระวังสิ่งที่ผิดพลาดมากขึ้นเนื่องจากมีเครือข่ายความปลอดภัยเริ่มต้นน้อยกว่าในภาษา


2
หมายเหตุ: โปรดอย่ายืนยันว่า C ++ มี destructic destructor Object Pascal / Delphi ยังมี destructic destructor แต่ยังรองรับ 'ในที่สุด' ด้วยเหตุผลที่ดีมากที่ฉันอธิบายในความคิดเห็นแรกของฉันด้านล่าง
เวกเตอร์

13
@ Mikey: เนื่องจากไม่เคยมีข้อเสนอให้เพิ่มfinallyมาตรฐาน C ++ ฉันคิดว่ามันปลอดภัยที่จะสรุปว่าชุมชน C ++ ไม่เห็นthe absence of finallyปัญหาใด ๆ ภาษาส่วนใหญ่ที่finallyขาดการทำลายล้างที่สอดคล้องกันที่ C ++ มี ฉันเห็นว่า Delphi มีพวกเขาทั้งสอง แต่ฉันไม่ทราบประวัติของมันดีพอที่จะรู้ว่ามีอยู่ที่ใดก่อน
Bart van Ingen Schenau

3
Dephi ไม่สนับสนุนวัตถุที่ใช้กองซ้อน - เฉพาะกองตามและอ้างอิงวัตถุบนกองซ้อน ดังนั้น 'ที่สุด' จึงจำเป็นต้องเรียกใช้ destructors อย่างชัดเจนและอื่น ๆ ตามความเหมาะสม
เวกเตอร์

2
มี cruft มากมายใน C ++ ที่ไม่จำเป็นต้องใช้เนื้อหาดังนั้นนี่จึงไม่ใช่คำตอบที่ถูกต้อง
Kaz

15
ในช่วงสองทศวรรษที่ผ่านมาฉันใช้ภาษานี้และทำงานกับคนอื่นที่ใช้ภาษานั้นฉันไม่เคยพบโปรแกรมเมอร์ C ++ ที่ใช้งานได้ซึ่งกล่าวว่า "ฉันหวังว่าภาษาจะมีfinally" ฉันไม่สามารถจำงานที่จะทำให้ง่ายขึ้นได้หรือไม่หากฉันสามารถเข้าถึงได้
Gort the Robot

12

คนอื่นพูดถึง RAII เป็นวิธีแก้ปัญหา มันเป็นทางออกที่ดีอย่างสมบูรณ์ แต่นั่นไม่ได้ระบุว่าทำไมพวกเขาถึงไม่เพิ่มfinallyเช่นกันเพราะมันเป็นสิ่งที่ต้องการอย่างกว้างขวาง คำตอบนั้นเป็นพื้นฐานในการออกแบบและพัฒนา C ++: ตลอดการพัฒนาของ C ++ ผู้ที่เกี่ยวข้องได้ต่อต้านการแนะนำคุณสมบัติการออกแบบที่สามารถทำได้โดยใช้คุณสมบัติอื่น ๆ โดยไม่ต้องยุ่งยากมาก ของคำหลักใหม่ที่อาจทำให้โค้ดที่เก่ากว่าไม่สามารถทำงานร่วมกันได้ เนื่องจาก RAII ให้ทางเลือกที่มีประสิทธิภาพสูงfinallyและคุณสามารถม้วนตัวคุณเองfinallyใน C ++ 11 ได้ดังนั้นจึงมีการเรียกใช้เล็กน้อย

สิ่งที่คุณต้องทำคือสร้างคลาสFinallyที่เรียกฟังก์ชันที่ส่งผ่านไปยัง constructor ของมันใน destructor จากนั้นคุณสามารถทำสิ่งนี้:

try
{
    Finally atEnd([&] () { database.close(); });

    database.doRisky();
}

โดยทั่วไปโปรแกรมเมอร์ภาษา C ++ ส่วนใหญ่จะชอบวัตถุของ RAII ที่ออกแบบมาอย่างหมดจด


3
คุณไม่มีข้อมูลการอ้างอิงในแลมบ์ดาของคุณ ควรจะเป็นFinally atEnd([&] () { database.close(); });เช่นนั้นฉันคิดว่าสิ่งต่อไปนี้จะดีกว่า: { Finally atEnd(...); try {...} catch(e) {...} }(ฉันยกตัวสุดท้ายออกจากบล็อกลองดังนั้นมันจะดำเนินการหลังจากบล็อก catch)
Thomas Eding

2

คุณสามารถใช้รูปแบบ "กับดัก" - แม้ว่าคุณไม่ต้องการใช้บล็อกลอง / catch

วางวัตถุอย่างง่ายในขอบเขตที่ต้องการ ใน destructor ของวัตถุนี้วางตรรกะ "finaly" ของคุณ ไม่ว่าจะเกิดอะไรขึ้นเมื่อสแต็กไม่คลายตัวจะมีการเรียกตัวทำลายวัตถุและคุณจะได้ขนมของคุณ


1
นี่ไม่ตอบคำถามและเพียงพิสูจน์ให้เห็นว่าในที่สุดก็ไม่ใช่ความคิดที่เลวร้ายเลย ...
Vector

2

คุณสามารถเรียงลำดับม้วนของคุณเองfinallyโดยใช้ Lambdas ซึ่งจะได้รับดังต่อไปนี้เพื่อรวบรวมค่าปรับ (ใช้ตัวอย่างที่ไม่มี RAII แน่นอนไม่ใช่รหัสที่ดีที่สุด):

{
    FILE *file = fopen("test","w");

    finally close_the_file([&]{
        cout << "We're closing the file in a pseudo-finally clause." << endl;
        fclose(file);
    });
}

ดูบทความนี้


-2

ผมไม่แน่ใจว่าผมเห็นด้วยกับยืนยันว่านี่คือ RAII superset finallyของ ส้นเท้าของจุดอ่อนของ RAII นั้นง่าย: มีข้อยกเว้น RAII ถูกนำไปใช้กับ destructors และมันผิดเสมอใน C ++ ที่จะโยนออกจาก destructor นั่นหมายความว่าคุณไม่สามารถใช้ RAII ได้เมื่อคุณต้องการให้โค้ดการล้างข้อมูลของคุณโยน หากfinallyมีการใช้งานในทางกลับกันก็ไม่มีเหตุผลที่จะเชื่อได้ว่ามันจะไม่ถูกกฎหมายที่จะโยนจากfinallyบล็อก

พิจารณาเส้นทางรหัสเช่นนี้

void foo() {
    try {
        ... stuff ...
        complex_cleanup();
    } catch (A& a) {
        handle_a(a);
        complex_cleanup();
        throw;
    } catch (B& b) {
        handle_b(b);
        complex_cleanup();
        throw;
    } catch (...) {
        handle_generic();
        complex_cleanup();
        throw;
    }
}

ถ้าเรามีfinallyเราสามารถเขียน:

void foo() {
    try {
        ... stuff ...
    } catch (A& a) {
        handle_a(a);
        throw;
    } catch (B& b) {
        handle_b(b);
        throw;
    } catch (...) {
        handle_generic();
        throw;
    } finally {
        complex_cleanup();
    }
}

แต่ไม่มีทางที่ฉันสามารถค้นหาเพื่อให้ได้พฤติกรรมที่เท่าเทียมกันโดยใช้ RAII

หากมีคนรู้วิธีการทำเช่นนี้ใน C ++ ฉันสนใจในคำตอบมาก ฉันยังมีความสุขกับสิ่งที่ต้องพึ่งพาเช่นบังคับให้ข้อยกเว้นทั้งหมดที่สืบทอดมาจากคลาสเดียวที่มีความสามารถพิเศษหรือบางอย่าง


1
ในตัวอย่างที่สองของคุณหากcomplex_cleanupสามารถโยนได้คุณอาจมีกรณีที่มีข้อยกเว้นที่ไม่ได้รับการตรวจสอบสองครั้งพร้อมกันเช่นเดียวกับที่คุณทำกับ RAII / destructors และ C ++ ปฏิเสธที่จะอนุญาตสิ่งนี้ หากคุณต้องการให้เห็นข้อยกเว้นดั้งเดิมคุณcomplex_cleanupควรป้องกันข้อยกเว้นใด ๆ เช่นเดียวกับที่มีกับ RAII / destructors หากคุณต้องการให้complex_cleanupมีข้อยกเว้นเกิดขึ้นฉันคิดว่าคุณสามารถใช้บล็อก try / catch แบบซ้อนได้ - แม้ว่านี่จะเป็นแบบแทนเจนต์และยากที่จะใส่ความคิดเห็นดังนั้นมันจึงคุ้มค่ากับคำถามที่แยกต่างหาก
Josh Kelley

ฉันต้องการใช้ RAII เพื่อรับพฤติกรรมที่เหมือนกันเป็นตัวอย่างแรกปลอดภัยยิ่งขึ้น โยนในสมมุติfinallyบล็อกจะทำงานอย่างชัดเจนเช่นเดียวกับโยนในที่catchบล็อก WRT ข้อยกเว้นในเที่ยวบิน - std::terminateไม่เรียก คำถามคือ "เพราะเหตุใดfinallyใน C ++" และคำตอบทั้งหมดบอกว่า "คุณไม่ต้องการมัน ... RAII FTW!" ประเด็นของฉันคือใช่แล้ว RAII ใช้ได้ดีสำหรับกรณีง่าย ๆ เช่นการจัดการหน่วยความจำ แต่จนกว่าปัญหาของข้อยกเว้นจะได้รับการแก้ไขต้องใช้ความคิด / ค่าใช้จ่าย / ความกังวล / การออกแบบใหม่มากเกินไปเพื่อเป็นโซลูชันทั่วไป
MadScientist

3
ฉันเข้าใจประเด็นของคุณ - มีปัญหาบางอย่างที่ถูกกฎหมายเกี่ยวกับ destructors ที่อาจเกิดขึ้น - แต่สิ่งเหล่านี้หายาก การพูดว่าข้อยกเว้น RAII + มีปัญหาที่ไม่ได้รับการแก้ไขหรือ RAII ไม่ใช่วิธีแก้ปัญหาทั่วไปก็ไม่ตรงกับประสบการณ์ของนักพัฒนา C ++ ส่วนใหญ่
Josh Kelley

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

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