คุณมีความสำคัญต่อความปลอดภัยของข้อยกเว้นในรหัส C ++ ของคุณหรือไม่


19

ทุกครั้งที่ฉันพิจารณาที่จะสร้างรหัสของฉันอย่างยิ่งยกเว้นที่ปลอดภัยฉันจะไม่ทำเพราะมันจะใช้เวลามาก ลองพิจารณาตัวอย่างข้อมูลที่ค่อนข้างง่าย:

Level::Entity* entity = new Level::Entity();
entity->id = GetNextId();
entity->AddComponent(new Component::Position(x, y));
entity->AddComponent(new Component::Movement());
entity->AddComponent(new Component::Render());
allEntities.push_back(entity); // std::vector
entityById[entity->id] = entity; // std::map
return entity;

ในการใช้การรับประกันข้อยกเว้นพื้นฐานฉันสามารถใช้ตัวชี้ที่กำหนดขอบเขตบนการnewโทร วิธีนี้จะป้องกันการรั่วไหลของหน่วยความจำหากมีการเรียกใด ๆ ที่จะทำให้เกิดข้อยกเว้น

อย่างไรก็ตามสมมติว่าฉันต้องการใช้การรับประกันข้อยกเว้นที่รัดกุม อย่างน้อยฉันจะต้องใช้ตัวชี้ที่ใช้ร่วมกันสำหรับบรรจุของฉัน (ฉันไม่ได้ใช้ Boost) nothrow Entity::Swapสำหรับการเพิ่มส่วนประกอบอะตอมและการจัดเรียงบางส่วนของสำนวนสำหรับอะตอมเพิ่มทั้งและVector Mapสิ่งเหล่านี้ไม่เพียงใช้เวลานานในการติดตั้ง แต่ยังมีราคาแพงเนื่องจากต้องใช้การคัดลอกมากกว่าโซลูชันที่ไม่ปลอดภัยยกเว้น

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

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


21
เมื่อฉันทำงานในโครงการ C ++ ในอดีตโดยทั่วไปเราทำข้อยกเว้นที่อ้างว่าไม่มี
Tetrad

2
โดยส่วนตัวแล้วฉันรักข้อยกเว้นและมักจะสนุกกับการหาวิธีทำให้รหัสของฉันให้การค้ำประกันที่แข็งแกร่ง หากคุณเพิ่งจะผิดพลาดเมื่อคุณได้รับการยกเว้นว่า ... อาจไม่รำคาญกับมัน

7
ฉันใช้ข้อยกเว้นในการจัดการ .. hm .. 'ข้อยกเว้น' ไม่ใช่ข้อผิดพลาด เช่นถ้าฉันรู้ว่าฟังก์ชั่นอาจทำงานล้มเหลวฉันปล่อยให้มันทำงานผิดพลาด แต่ถ้าฉันรู้ว่าฟังก์ชั่นอาจอ่านไฟล์เก็บถาวรไม่สำเร็จ แต่มีอีกวิธีหนึ่ง และถ้ามันเป็นข้อยกเว้น "อ่านไม่ได้" ฉันก็จัดการมันในวิธีอื่นที่ฉันจะทำโดยไม่ต้องอ่านไฟล์เก็บถาวร
Gustavo Maciel

สิ่งที่ Gtoknu พูดคุณจำเป็นต้องระวังว่ามีข้อยกเว้นสำหรับบรรณารักษ์ภายนอกใดบ้าง
ปีเตอร์Ølsted

เกี่ยวข้อง: gamedev.stackexchange.com/questions/2762/…
Tetrad

คำตอบ:


26

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

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

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

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


12
เพียงอย่างเดียวนี้มีค่า +1: "รหัสที่ไม่ได้ใช้ข้อยกเว้นและไม่ปลอดภัยยกเว้นข้อยกเว้นจะดีกว่ารหัสที่ใช้พวกเขา แต่ครึ่งมั่นใจในความปลอดภัยข้อยกเว้น"
Maximus Minimus

14

กฎทั่วไปของหัวแม่มือของฉัน: หากคุณต้องการใช้ข้อยกเว้นให้ใช้ข้อยกเว้น หากคุณไม่จำเป็นต้องใช้ข้อยกเว้นอย่าใช้ข้อยกเว้น หากคุณไม่ทราบว่าจำเป็นต้องใช้ข้อยกเว้นหรือไม่คุณไม่จำเป็นต้องใช้ข้อยกเว้น

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

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


5

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

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

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

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

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

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

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


2

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

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

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

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

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

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


2

คุณต้องดูข้อยกเว้นของคุณและต้องดูเงื่อนไขที่สามารถทริกเกอร์ได้ นรกเป็นจำนวนมากเวลาส่วนใหญ่ของสิ่งที่คนใช้ข้อยกเว้นสำหรับสามารถหลีกเลี่ยงได้โดยการตรวจสอบที่เหมาะสมในระหว่างรอบการพัฒนา / การดีบักการสร้าง / การทดสอบ ใช้ asserts, ผ่านการอ้างอิง, เตรียมใช้งานตัวแปรโลคัลเสมอในการประกาศ, memset-zero การจัดสรรทั้งหมด, NULL หลังฟรี, และอื่น ๆ และกรณีข้อยกเว้นเชิงทฤษฎีจำนวนมากก็หายไป

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


1

ไม่แน่ใจว่าการอ้างถึงคนอื่นมีความเหมาะสมใน SE หรือไม่ แต่ฉันรู้สึกว่าไม่มีคำตอบอื่นใดที่สัมผัสกับสิ่งนี้จริงๆ

อ้างถึง Jason Gregory จากหนังสือGame Engine Architecture ของเขา (หนังสือดีมาก!)

"Structured exception handling (SEH) เพิ่มโอเวอร์เฮดจำนวนมากให้กับโปรแกรมทุก ๆ เฟรมสแต็กจะต้องเพิ่มเพื่อให้มีข้อมูลเพิ่มเติมที่ต้องการโดยกระบวนการคลี่คลายสแต็กนอกจากนี้สแต็กคลายมักจะช้ามาก - ตามลำดับสองถึงสาม เวลามีราคาแพงกว่าเพียงแค่กลับจากฟังก์ชันนอกจากนี้หากแม้แต่หนึ่งฟังก์ชันในโปรแกรมของคุณ (หรือไลบรารี) ใช้ SEH โปรแกรมทั้งหมดของคุณต้องใช้ SEH คอมไพเลอร์ไม่สามารถรู้ได้ว่าฟังก์ชันใดจะอยู่เหนือคุณใน call stack เมื่อ คุณโยนข้อยกเว้น "

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


2
การจัดการข้อยกเว้นแบบมีโครงสร้างเป็นชนิดของการจัดการข้อยกเว้นเฉพาะที่มีอยู่ใน Windows ซึ่งไม่เหมือนกับข้อยกเว้น C ++ โดยทั่วไป
Kylotan

ฉันไม่อยากจะเชื่อเลยว่าไม่รู้ ขอบคุณสำหรับการแก้ไข!
Klashnikov เด็ก

0

ฉันทำให้รหัสข้อยกเว้นของฉันปลอดภัยเสมอเพราะมันทำให้ง่ายต่อการทราบว่าเกิดปัญหาขึ้นได้อย่างไรเนื่องจากฉันสามารถบันทึกสิ่งที่เกิดขึ้นเมื่อใดก็ตามที่มีการสร้างหรือจับข้อยกเว้น

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