มันสมเหตุสมผลที่จะป้องกัน null ทุกตัวชี้ dereferenced หรือไม่


65

ในงานใหม่ฉันได้รับการตั้งค่าสถานะในการตรวจสอบรหัสสำหรับรหัสเช่นนี้:

PowerManager::PowerManager(IMsgSender* msgSender)
  : msgSender_(msgSender) { }

void PowerManager::SignalShutdown()
{
    msgSender_->sendMsg("shutdown()");
}

ฉันบอกว่าวิธีสุดท้ายควรอ่าน:

void PowerManager::SignalShutdown()
{
    if (msgSender_) {
        msgSender_->sendMsg("shutdown()");
    }
}

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

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

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

แก้ไข : ในตัวอย่างข้างต้นผู้msgSender_ทำงานร่วมกันไม่จำเป็น หากเป็นNULLไปได้แสดงว่ามีข้อผิดพลาด เหตุผลเดียวที่ส่งผ่านไปยัง Constructor นั้นPowerManagerสามารถทดสอบได้ด้วยIMsgSenderคลาสย่อยจำลอง

สรุป : มีคำตอบที่ดีจริงๆสำหรับคำถามนี้ขอบคุณทุกคน ฉันยอมรับหนึ่งจาก @aaronps ส่วนใหญ่เนื่องจากความกะทัดรัด ดูเหมือนจะมีข้อตกลงทั่วไปที่ค่อนข้างกว้างว่า:

  1. การบังคับใช้NULLยามสำหรับตัวชี้ที่ถูกอ้างถึงทุกตัวนั้นเป็นจำนวนมากเกินไป แต่
  2. คุณสามารถดำเนินการอภิปรายทั้งหมดโดยใช้การอ้างอิงแทน (ถ้าเป็นไปได้) หรือconstตัวชี้และ
  3. assertคำสั่งเป็นทางเลือกที่รู้แจ้งมากขึ้นNULLสำหรับเจ้าหน้าที่เพื่อตรวจสอบว่ามีการปฏิบัติตามเงื่อนไขเบื้องต้นของฟังก์ชัน

14
อย่าคิดเลยว่าความผิดพลาดเป็นโดเมนของโปรแกรมเมอร์รุ่นเยาว์ 95% ของนักพัฒนาทุกระดับมีประสบการณ์ทำอะไรซักอย่างครั้งเดียว ส่วนที่เหลืออีก 5% กำลังโกหกฟันของพวกเขา
Blrfl

56
ถ้าฉันเห็นใครบางคนเขียนโค้ดที่ตรวจสอบว่ามีค่าใดและล้มเหลวอย่างเงียบ ๆ ฉันก็ต้องไล่พวกมันออกทันที
Winston Ewert

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

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

6
@Rig ฉันแน่ใจว่ามีกรณีที่เหมาะสมสำหรับความล้มเหลวเงียบ แต่ถ้ามีคนทำให้ความเงียบล้มเหลวในการปฏิบัติทั่วไป (เว้นแต่ทำงานในดินแดนที่เหมาะสม) ฉันไม่ต้องการทำงานในโครงการที่พวกเขาวางโค้ดไว้
Winston Ewert

คำตอบ:


66

มันขึ้นอยู่กับ 'สัญญา':

หากPowerManager ต้องมีข้อมูลที่ถูกต้องIMsgSenderอย่าตรวจสอบ null ให้ปล่อยให้มันตายเร็วขึ้น

หากในอีกทางหนึ่งมันอาจมีIMsgSenderแล้วคุณต้องตรวจสอบทุกครั้งที่คุณใช้ง่ายเหมือนที่

ความคิดเห็นสุดท้ายเกี่ยวกับเรื่องราวของโปรแกรมเมอร์รุ่นจูเนียร์ปัญหาคือการขาดขั้นตอนการทดสอบ


4
+1 สำหรับคำตอบที่รวบรัดมากซึ่งเป็นผลรวมของวิธีการที่ฉันรู้สึก: "มันขึ้นอยู่กับ ... " คุณยังมีความกล้าที่จะพูดว่า "ไม่เคยตรวจสอบโมฆะ" ( ถ้าโมฆะไม่ถูกต้อง) การวางassert()ฟังก์ชั่นสมาชิกทุกคนที่มีการชี้ตัวชี้เป็นการประนีประนอมที่มีแนวโน้มที่จะปลอบโยนผู้คนจำนวนมาก แต่ถึงแม้จะรู้สึกว่า 'หวาดระแวงเก็งกำไร' สำหรับฉัน รายการสิ่งที่เกินความสามารถในการควบคุมของฉันนั้นไม่มีที่สิ้นสุดและเมื่อฉันยอมให้ตัวเองเริ่มกังวลเกี่ยวกับสิ่งเหล่านั้นมันจะกลายเป็นความลาดชันที่ลื่นมาก การเรียนรู้ที่จะเชื่อถือการทดสอบของฉันเพื่อนร่วมงานของฉันและดีบักเกอร์ของฉันได้ช่วยให้ฉันนอนหลับได้ดีขึ้นในเวลากลางคืน
หลบเลี่ยง

2
เมื่อคุณอ่านโค้ดการยืนยันเหล่านั้นมีประโยชน์มากในการทำความเข้าใจว่ารหัสควรทำงานอย่างไร ฉันไม่เคยเข้ารหัส codebase ที่ทำให้ฉันไป 'ฉันหวังว่ามันจะไม่ได้มีการยืนยันทั้งหมดเหล่านี้ทั่วสถานที่' แต่ฉันเห็นมากมายที่ฉันพูดตรงข้าม
Kristof Provost

1
ธรรมดาและเรียบง่ายนี่คือคำตอบที่ถูกต้องสำหรับคำถาม
Matthew Azkimov

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

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

77

ฉันรู้สึกว่ารหัสควรอ่าน:

PowerManager::PowerManager(IMsgSender* msgSender)
  : msgSender_(msgSender)
{
    assert(msgSender);
}

void PowerManager::SignalShutdown()
{
    assert(msgSender_);
    msgSender_->sendMsg("shutdown()");
}

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

"ปัญญา" ที่แบ่งปันของเพื่อนร่วมงานของคุณจะไม่สนใจข้อผิดพลาดนี้อย่างเงียบ ๆ พร้อมกับผลลัพธ์ที่ไม่คาดคิด

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

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


10
ความแตกต่างที่สำคัญระหว่างการเรียก assert และตัวอย่างรหัสของ OP คือ assert นั้นเปิดใช้งานเฉพาะใน debug builds (#define NDEBUG) เมื่อผลิตภัณฑ์เข้าสู่มือลูกค้าและ debug จะถูกปิดการใช้งาน ตัวชี้โมฆะแม้ว่าฟังก์ชั่นที่เป็นปัญหาจะถูกดำเนินการยืนยันยืนยันให้คุณไม่มีการป้องกัน
atk

17
สันนิษฐานว่าคุณได้ทดสอบการสร้างก่อนส่งจริงให้กับลูกค้า แต่ใช่นี่เป็นความเสี่ยงที่เป็นไปได้ วิธีแก้ปัญหานั้นง่าย: เพียงแค่ส่งพร้อมเปิดใช้งาน asserts (ซึ่งเป็นรุ่นที่คุณทดสอบอยู่แล้ว) หรือแทนที่ด้วยแมโครของคุณเองซึ่งยืนยันในโหมดดีบักและเพียงแค่บันทึกล็อก + backtraces ออก ... ในโหมดการเปิดตัว
Kristof Provost

7
@James - ใน 90% ของกรณีคุณจะไม่กู้คืนจากการตรวจสอบ NULL ที่ล้มเหลวเช่นกัน แต่ในกรณีเช่นนี้รหัสจะล้มเหลวอย่างเงียบ ๆ และข้อผิดพลาดอาจปรากฏขึ้นในภายหลัง - ตัวอย่างเช่นคุณคิดว่าคุณบันทึกไว้ในไฟล์ คุณไม่สามารถกู้คืนจากทุกสถานการณ์ที่จะต้องไม่เกิดขึ้น คุณสามารถตรวจสอบว่าพวกเขาจะไม่เกิดขึ้น แต่ฉันไม่คิดว่าคุณมีงบประมาณเว้นแต่คุณจะเขียนโค้ดสำหรับดาวเทียมนาซ่าหรือโรงไฟฟ้านิวเคลียร์ คุณต้องมีวิธีการพูดว่า "ฉันไม่รู้ว่าเกิดอะไรขึ้น - โปรแกรมไม่น่าจะอยู่ในสถานะนี้"
Maciej Piechotka

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

2
@ James: จากประสบการณ์ของฉันกับระบบฝังตัวซอฟต์แวร์และฮาร์ดแวร์ได้รับการออกแบบอย่างคงเส้นคงวาซึ่งยากที่จะทำให้อุปกรณ์ใช้งานไม่ได้อย่างสมบูรณ์ ในกรณีส่วนใหญ่มีการจ้องจับผิดฮาร์ดแวร์ที่บังคับให้รีเซ็ตอุปกรณ์อย่างสมบูรณ์หากซอฟต์แวร์หยุดทำงาน
Bart van Ingen Schenau

30

หาก msgSender ไม่เคยเป็นnullคุณควรใส่nullเช็คในคอนสตรัคเตอร์เท่านั้น สิ่งนี้ก็เป็นจริงเช่นกันสำหรับอินพุตอื่น ๆ ในคลาส - ใส่การตรวจสอบความสมบูรณ์ ณ จุดที่เข้าสู่ 'โมดูล' - คลาส, ฟังก์ชั่น ฯลฯ

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

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


11

ไม่มีก็ไม่สมเหตุสมผลที่จะตรวจสอบแต่ละคนและทุกตัวชี้ dereference NULLสำหรับเป็นตัวชี้

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

หากฉันเผชิญกับสถานการณ์เช่นคุณฉันจะถามคำถามสองข้อ:

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

9

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

ในสถานการณ์เช่นนี้ฉันต้องการเปลี่ยนอินเทอร์เฟซเพื่อให้ความต้องการของผู้โทรถูกบังคับใช้โดยภาษา:

PowerManager::PowerManager(const IMsgSender& msgSender)
  : msgSender_(msgSender) {}

void PowerManager::SignalShutdown() {
    msgSender_->sendMsg("shutdown()");
}

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

นอกจากนี้คุณยังสามารถเขียนสิ่งเดียวกันโดยใช้ตัวชี้อัจฉริยะ (ผ่านการเพิ่มหรือ c ++ 11) เพื่อบังคับIMsgSenderให้ใช้งานได้นานกว่าPowerManager:

PowerManager::PowerManager(std::shared_ptr<IMsgSender> msgSender) 
  : msgSender_(msgSender) {}

void PowerManager::SignalShutdown() {
    // Here, we own a smart pointer to IMsgSender, so even if the caller
    // destroys the original pointer, we still have a valid copy
    msgSender_->sendMsg("shutdown()");
}

วิธีนี้เป็นที่ต้องการหากเป็นไปได้ที่IMsgSenderจะไม่สามารถรับประกันได้ว่าอายุการใช้งานจะนานกว่าPowerManager(เช่นx = new IMsgSender(); p = new PowerManager(*x);)

†ในเรื่องเกี่ยวกับพอยน์เตอร์: การตรวจสอบโมฆะอาละวาดทำให้การอ่านโค้ดยากขึ้นและไม่ปรับปรุงเสถียรภาพ (มันช่วยปรับปรุงลักษณะที่ปรากฏของความเสถียร

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

เนื่องจาก the PowerManagerไม่ได้เป็นเจ้าของIMsgSender(มันแค่ยืมมาระยะหนึ่ง) มันจะไม่รับผิดชอบในการจัดสรรหรือทำลายหน่วยความจำนั้น นี่คืออีกเหตุผลหนึ่งที่ฉันชอบการอ้างอิง

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


1
จริงๆแล้วไม่มีอะไรผิดปกติกับการใช้พอยน์เตอร์ดิบในบางครั้ง Std :: shared_ptr ไม่ใช่ bullet เงินและใช้มันในรายการอาร์กิวเมนต์เป็นวิธีการแก้ปัญหาด้านวิศวกรรมที่เลอะเทอะแม้ว่าฉันจะรู้ว่ามันเป็น 'leet' ที่จะใช้มันทุกครั้งที่เป็นไปได้ ใช้จาวาถ้าคุณรู้สึกเช่นนั้น
James

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

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

8

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

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

เมื่อใดก็ตามที่มีการเรียกใช้ฟังก์ชั่นและไม่ใช่พฤติกรรมที่เป็นตัวเลือกมันควรล้มเหลวอย่างมาก

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


7

กับคนอื่น ๆ ได้ตั้งข้อสังเกตนี้ขึ้นอยู่กับว่าหรือไม่msgSenderสามารถที่จะถูกต้องตามกฎหมาย NULLต่อไปนี้จะถือว่าไม่ควรเป็น NULL

void PowerManager::SignalShutdown()
{
    if (!msgSender_)
    {
       throw SignalException("Shut down failed because message sender is not set.");
    }

    msgSender_->sendMsg("shutdown()");
}

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

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

วิธีใดวิธีหนึ่งเหล่านี้จะหลีกเลี่ยงความล้มเหลวที่เงียบ:

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

  2. โยนข้อยกเว้นถ้ามันเป็นโมฆะ


5

ฉันเห็นด้วยกับดักโมฆะในตัวสร้าง เพิ่มเติมหากสมาชิกถูกประกาศในส่วนหัวเป็น:

IMsgSender* const msgSender_;

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


นั่นคือคำแนะนำที่ดีขอบคุณ! จริงๆแล้วฉันขอโทษฉันไม่สามารถโหวตให้สูงกว่านี้ได้อีกแล้ว มันไม่ได้เป็นคำตอบที่ตรงไปที่คำถามที่ระบุไว้ แต่มันเป็นที่ดีวิธีที่จะขจัดความเป็นไปของตัวชี้ใด ๆ 'บังเอิญ' NULLกลายเป็น
หลบเลี่ยง

@ กระแสฉันคิดว่า "ฉันเห็นด้วยกับคนอื่น" ตอบแรงขับหลักของคำถาม ไชโย แต่! ;-)
Grimm The Opiner

ฉันจะไม่สุภาพเล็กน้อยที่จะเปลี่ยนคำตอบที่ได้รับการยอมรับ ณ จุดนี้เนื่องจากวิธีการตั้งคำถาม แต่ฉันคิดว่าคำแนะนำนี้ยอดเยี่ยมจริง ๆ และฉันขอโทษฉันไม่คิดด้วยตัวเอง ด้วยconstตัวชี้ไม่จำเป็นต้องassert()อยู่นอกนวกรรมิกดังนั้นคำตอบนี้ดูเหมือนจะดีกว่า @Kristof Provost's (ซึ่งก็ยอดเยี่ยม แต่ก็ตอบคำถามโดยอ้อมเช่นกัน) ฉันหวังว่าคนอื่นจะลงคะแนนนี้เพราะมัน จริงๆไม่ได้ทำให้รู้สึกถึงในทุกวิธีเมื่อคุณก็สามารถทำให้ตัวชี้assert const
หลบเลี่ยง

@evadeflow ผู้คนเข้าชม Questins สำหรับตัวแทนเท่านั้นตอนนี้ได้รับคำตอบแล้วจะไม่มีใครดูอีกเลย ;-) ฉันเพียงโผล่เข้ามาเพราะมันเป็นคำถามเกี่ยวกับพอยน์เตอร์และฉันคอยดูเลือด "ใช้พอยน์เตอร์พอยน์เตอร์อัจฉริยะสำหรับทุกสิ่งเสมอ"
Grimm The Opiner

3

นี่เป็นอันตรายจริงๆ!

ฉันทำงานภายใต้นักพัฒนาอาวุโสใน C codebase ด้วย "มาตรฐาน" shoddiest ที่ผลักดันให้ทำสิ่งเดียวกันเพื่อตรวจสอบตัวชี้ทั้งหมดว่าไม่มีค่า นักพัฒนาจะต้องทำสิ่งต่าง ๆ เช่นนี้:

// Pre: vertex should never be null.
void transform_vertex(Vertex* vertex, ...)
{
    // Inserted by my "wise" co-worker.
    if (!vertex)
        return;
    ...
}

ฉันเคยลองลบการตรวจสอบเงื่อนไขเบื้องต้นหนึ่งครั้งในฟังก์ชั่นดังกล่าวและแทนที่ด้วยการassertดูว่าจะเกิดอะไรขึ้น

สำหรับความสยองขวัญของฉันฉันพบโค้ดหลายพันบรรทัดใน codebase ซึ่งส่งผ่าน nulls ไปยังฟังก์ชันนี้ แต่ที่นักพัฒนาอาจสับสนทำงานรอบ ๆ และเพิ่งเพิ่มโค้ดเพิ่มเติมจนกว่าสิ่งต่างๆจะทำงาน

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

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

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

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

มันเป็นเหตุผลที่มาตรฐานการเข้ารหัสที่จะต้องตรวจสอบทุกตัวชี้ dereferenced ในฟังก์ชั่นสำหรับ NULL แรก - แม้แต่สมาชิกข้อมูลส่วนตัว?

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

assertเป็นวิธีที่จะไปที่นี่ การละเมิดเงื่อนไขไม่ควรได้รับอนุญาตให้ไปโดยไม่มีใครสังเกตไม่เช่นนั้นกฎของเมอร์ฟีก็สามารถเตะเข้ามาได้อย่างง่ายดาย


1
"ยืนยัน" เป็นวิธีการที่จะไป - แต่จำไว้ว่าในระบบที่ทันสมัยใด ๆ การเข้าถึงหน่วยความจำแต่ละครั้งผ่านตัวชี้มีการยืนยันตัวชี้โมฆะตัวที่สร้างขึ้นในฮาร์ดแวร์ ดังนั้นใน "assert (p! = NULL); return * p;" แม้แต่การยืนยันก็ไม่มีจุดหมาย
gnasher729

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

@ gnasher729 หรือฉันอาจเข้าใจผิดบางส่วน ระบบที่ทันสมัยเหล่านี้แสดงรหัสแหล่งบรรทัดที่การยืนยันล้มเหลวหรือไม่ โดยทั่วไปฉันคิดว่าพวกเขาสูญเสียข้อมูล ณ จุดนั้นและอาจแสดง segfault / การละเมิดการเข้าถึง แต่ฉันค่อนข้างห่างจาก "ทันสมัย" - ยังคงดื้อรั้นใน Windows 7 สำหรับการพัฒนาส่วนใหญ่ของฉัน

ตัวอย่างเช่นบน MacOS X และ iOS หากแอปของคุณขัดข้องเนื่องจากการเข้าถึงตัวชี้ null ในระหว่างการพัฒนาดีบักเกอร์จะบอกบรรทัดรหัสที่แน่นอนให้คุณทราบ มีอีกแง่มุมที่แปลกประหลาดคือคอมไพเลอร์จะพยายามเตือนคุณหากคุณทำสิ่งที่น่าสงสัย หากคุณส่งตัวชี้ไปยังฟังก์ชันและเข้าถึงคอมไพเลอร์จะไม่ให้คำเตือนว่าตัวชี้อาจเป็น NULL เนื่องจากเกิดขึ้นบ่อยครั้งคุณจะจมน้ำตายในคำเตือน อย่างไรก็ตามถ้าคุณทำการเปรียบเทียบถ้า (p == NULL) ... จากนั้นคอมไพเลอร์จะถือว่ามีโอกาสที่ดีที่ p คือ NULL
gnasher729

เพราะเหตุใดคุณจึงต้องทดสอบเป็นอย่างอื่นดังนั้นจึงจะให้คำเตือนตั้งแต่นั้นมาหากคุณใช้โดยไม่ทำการทดสอบ หากคุณใช้มาโครที่มีลักษณะเหมือนยืนยันคุณต้องฉลาดสักหน่อยในการเขียนเพื่อให้ "my_assert (p! = NULL" เป็นตัวชี้โมฆะโง่! "); * p = 1;" ไม่เตือนคุณ
gnasher729

2

ยกตัวอย่างเช่นObjective-Cใช้การเรียกใช้เมธอดบนnilอ็อบเจ็กต์เป็น no-op ที่ประเมินค่าเป็นศูนย์ มีข้อดีบางประการในการตัดสินใจออกแบบใน Objective-C เนื่องจากเหตุผลที่แนะนำในคำถามของคุณ แนวคิดเชิงทฤษฎีของการป้องกันค่าว่างทุกการเรียกใช้เมธอดมีข้อดีอยู่บ้างหากมีการเผยแพร่และนำไปใช้อย่างต่อเนื่อง

ที่กล่าวว่ารหัสชีวิตในระบบนิเวศไม่ใช่สุญญากาศ พฤติกรรมที่มีการป้องกันแบบ null จะไม่เป็นไปในทางที่ผิดและน่าประหลาดใจใน C ++ ดังนั้นจึงควรถือว่าเป็นอันตราย โดยสรุปแล้วไม่มีใครเขียนรหัส C ++ ด้วยวิธีนี้ดังนั้นอย่าทำ! อย่างไรก็ตามตามตัวอย่างเคาน์เตอร์โปรดทราบว่าการโทรfree()หรือdeleteบนNULLใน C และ C ++ รับประกันว่าจะไม่ใช้งาน


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

ยิ่งไปกว่านั้นสร้างควรยอมรับการอ้างอิงที่ไม่อาจจะconst IMsgSender&NULL


1

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

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

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

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

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


+1 เมื่อมีความกล้าที่จะสนับสนุนตำแหน่งที่ไม่เป็นที่นิยม ฉันจะผิดหวังถ้าไม่มีใครปกป้องเพื่อนร่วมงานของฉันเกี่ยวกับเรื่องนี้ (พวกเขาเป็นคนที่ฉลาดมากที่ได้วางผลิตภัณฑ์ที่มีคุณภาพเป็นเวลาหลายปี) ฉันชอบความคิดที่ว่าแอพควร "ผิดพลาดในสภาพแวดล้อมการทดสอบ ... และไม่สามารถผลิตได้อย่างงดงาม" ผมไม่เชื่อว่าอิง 'ท่องจำ' NULLการตรวจสอบเป็นวิธีที่จะทำอย่างนั้น แต่ฉันยินดีรับฟังความเห็นจากคนที่อยู่นอกสถานที่ทำงานของฉันที่เป็นพื้นพูดว่า: "Aigh ดูเหมือนจะไม่เกินไป . ไม่มีเหตุผล"
evadeflow

ฉันรำคาญเมื่อเห็นผู้คนทดสอบ NULL หลัง malloc () มันบอกฉันว่าพวกเขาไม่เข้าใจว่าระบบปฏิบัติการสมัยใหม่จัดการหน่วยความจำได้อย่างไร
Kristof Provost

2
ฉันเดาว่า @KristofProvost กำลังพูดถึงระบบปฏิบัติการที่ใช้ overcommit ซึ่ง malloc จะประสบความสำเร็จเสมอ (แต่ระบบปฏิบัติการอาจฆ่ากระบวนการของคุณในภายหลังหากหน่วยความจำไม่เพียงพอจริง ๆ ) อย่างไรก็ตามนี่ไม่ใช่เหตุผลที่ดีที่จะข้ามการตรวจสอบ null บน malloc: overcommit ไม่เป็นสากลข้ามแพลตฟอร์มและแม้ว่าจะเปิดใช้งานก็ตามอาจมีสาเหตุอื่น ๆ ที่ malloc ล้มเหลว (เช่นบน Linux ซึ่งเป็นขีด จำกัด พื้นที่ที่อยู่ของกระบวนการที่ตั้งค่า ulimit )
John Bartholomew

1
สิ่งที่จอห์นพูด ฉันกำลังพูดถึง overcommit Overcommit ถูกเปิดใช้งานโดยค่าเริ่มต้นบน Linux (ซึ่งเป็นสิ่งที่จ่ายค่าของฉัน) ฉันไม่รู้ แต่สงสัยว่าถ้ามันเหมือนกันใน Windows ในกรณีส่วนใหญ่คุณจะจบลงด้วยการหยุดทำงานเว้นแต่ว่าคุณพร้อมที่จะเขียนโค้ดจำนวนมากที่ไม่เคยทดสอบเพื่อจัดการกับข้อผิดพลาดที่แทบจะไม่เกิดขึ้นแน่นอน ...
Kristof Provost

2
ให้ฉันเพิ่มว่าฉันไม่เคยเห็นรหัส (นอกเคอร์เนลลินุกซ์ที่หน่วยความจำไม่เพียงพอที่จะเป็นไปได้จริง) ที่จริงจะจัดการสภาพหน่วยความจำไม่ถูกต้อง รหัสทั้งหมดที่ฉันเคยเห็นลองจะล้มเหลวในภายหลัง สิ่งที่ทำได้คือการเสียเวลาทำให้รหัสยากต่อการเข้าใจและซ่อนปัญหาจริง การกำหนดค่า Null นั้นง่ายต่อการดีบัก (ถ้าคุณมีไฟล์แกน)
Kristof Provost

1

การใช้ตัวชี้แทนการอ้างอิงจะบอกฉันว่าmsgSenderเป็นเพียงตัวเลือกและที่นี่การตรวจสอบโมฆะจะเป็นสิ่งที่ถูกต้อง ข้อมูลโค้ดช้าเกินไปที่จะตัดสินใจ อาจมีองค์ประกอบอื่น ๆPowerManagerที่มีค่า (หรือทดสอบได้) ...

เมื่อเลือกระหว่างตัวชี้และการอ้างอิงฉันชั่งน้ำหนักตัวเลือกทั้งสองอย่างละเอียด ถ้าฉันต้องใช้พอยน์เตอร์สำหรับสมาชิก (แม้แต่สมาชิกส่วนตัว) ฉันต้องยอมรับif (x)โพรซีเดอร์ทุกครั้งที่ฉันลงทะเบียน


ดังที่ฉันพูดถึงในความคิดเห็นอื่นบางแห่งมีบางครั้งที่การฉีดการอ้างอิงเป็นไปไม่ได้ ตัวอย่างที่ฉัน
พบเจอบ่อยๆ

@evflow: ตกลงฉันยอมรับมันขึ้นอยู่กับขนาดของชั้นเรียนและการมองเห็นของสมาชิกตัวชี้ (ที่ไม่สามารถทำการอ้างอิงได้) ไม่ว่าฉันจะตรวจสอบก่อนการประชุม ในกรณีที่มีชั้นเรียนมีขนาดเล็กและธรรมดาและตัวชี้ (s) ส่วนตัวผมทำไม่ได้ ...
หมาป่า

0

ขึ้นอยู่กับสิ่งที่คุณต้องการจะเกิดขึ้น

คุณเขียนว่าคุณกำลังทำงานกับ "อุปกรณ์อิเล็กทรอนิกส์สำหรับผู้บริโภค" ถ้าอย่างใดข้อผิดพลาดจะถูกนำเสนอโดยการตั้งค่าmsgSender_เป็นNULLคุณต้องการ

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

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

แน่นอนถ้าคุณเลือกตัวเลือก 1 ข้อassert(ตามที่คนอื่นแนะนำ) มีความสำคัญต่อการลดโอกาสในการเกิดข้อผิดพลาดที่ไม่คืบหน้าในระหว่างการพัฒนา ifป้องกัน null มีไว้เพื่อลดความล้มเหลวในการใช้งานจริง

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

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