การตรวจสอบตัวชี้ไม่ปลอดภัยNULL
โดยการเขียนเพียงอย่างเดียวif(pointer)
หรือฉันต้องใช้อย่างปลอดภัยif(pointer != NULL)
หรือไม่?
NULL
ใน C ++ จากที่นี้เพราะNULL
เป็นมาโครที่ขึ้นกับการใช้งานซึ่งอาจทำให้คุณมีพฤติกรรมที่ไม่ชัดเจน
การตรวจสอบตัวชี้ไม่ปลอดภัยNULL
โดยการเขียนเพียงอย่างเดียวif(pointer)
หรือฉันต้องใช้อย่างปลอดภัยif(pointer != NULL)
หรือไม่?
NULL
ใน C ++ จากที่นี้เพราะNULL
เป็นมาโครที่ขึ้นกับการใช้งานซึ่งอาจทำให้คุณมีพฤติกรรมที่ไม่ชัดเจน
คำตอบ:
คุณสามารถ; ตัวชี้โมฆะจะถูกแปลงเป็นบูลีนโดยปริยายในขณะที่พอยน์เตอร์ที่ไม่ใช่นัลถูกแปลงเป็นจริง จากมาตรฐาน C ++ 11 ส่วนในBoolean Conversions:
prvalue ของเลขคณิตนับ unscoped ตัวชี้หรือตัวชี้ไปยังประเภทสมาชิกสามารถแปลงเป็น prvalue
bool
ของประเภท ค่าศูนย์ค่าตัวชี้โมฆะหรือค่าตัวชี้โมฆะสมาชิกถูกแปลงเป็นfalse
; ค่าอื่น ๆtrue
จะถูกแปลงเป็น prvalue ชนิดstd::nullptr_t
สามารถแปลงเป็น prvalue ชนิดbool
;false
คุ้มค่าที่เกิดขึ้นคือ
ใช่คุณทำได้
นี่เป็นส่วนหนึ่งของการแปลงมาตรฐาน C ++ ซึ่งอยู่ในมาตราการแปลงบูลีน :
§ 4.12 การแปลงบูลีน
prvalue ของเลขคณิตการแจงนับที่ไม่ จำกัด ขอบเขตตัวชี้หรือตัวชี้เป็นชนิดสมาชิกสามารถแปลงเป็น prvalue ของบูลประเภท ค่าศูนย์ค่าตัวชี้โมฆะหรือค่าตัวชี้โมฆะสมาชิกถูกแปลงเป็นเท็จ ค่าอื่นใดจะถูกแปลงเป็นจริง prvalue ของ type std :: nullptr_t สามารถแปลงเป็น prvalue ของ type bool; ค่าผลลัพธ์เป็นเท็จ
ใช่คุณสามารถ. ที่จริงแล้วฉันชอบที่จะใช้if(pointer)
เพราะมันง่ายกว่าในการอ่านและเขียนเมื่อคุณชินกับมันแล้ว
นอกจากนี้ยังทราบว่า C ++ 11 แนะนำซึ่งเป็นที่ต้องการมากกว่าnullptr
NULL
if(som_integer)
vs ได้if(some_integer != 0)
เพราะจำนวนเต็มไม่ใช่บูลีนใช่ไหม? ฉันชอบที่จะหลีกเลี่ยง0
หรือNULL
ในคำสั่ง if
if (pointer)
ตัวเองมากกว่า แต่if (ptr != nullptr)
ดูเหมือนว่าถูกต้องตามกฎหมายสำหรับฉัน ในทางตรงกันข้ามถ้าผมเห็นใครบางคนในทีมของฉันที่เขียนผมจะทำให้พวกเขาเปลี่ยนไปif (some_integer)
if (some_integer != 0)
อย่างไรก็ตามฉันจะไม่แกล้งทำเป็นว่ามันไม่ใช่ความชอบโดยพลการในส่วนของฉัน - ฉันไม่ชอบที่จะรักษาพอยน์เตอร์และจำนวนเต็มเหมือนกัน
if(isReady)
if(filePtr)
if(serviceNo)
เหรอ การตั้งชื่อตัวแปรที่ไม่ดีตามวัตถุประสงค์นั้นไม่ได้มีความหมายมากนัก อย่างไรก็ตามฉันมีจุดของคุณและเข้าใจแล้ว แต่ฉันสามารถยืนยันโดยใช้รูปแบบการเข้ารหัสของฉันเองได้ไหม
ตอบคำถามแล้ว แต่ฉันต้องการเพิ่มคะแนน
ฉันจะชอบif(pointer)
มากกว่าif(pointer != NULL)
และif(!pointer)
แทนที่จะเป็นif(pointer == NULL)
:
โอกาสน้อยที่จะเขียนรหัสรถสมมติว่าถ้าผมสะกดผู้ประกอบการตรวจสอบความเท่าเทียมกัน==
กับ=
if(pointer == NULL)
สามารถสะกดผิดดังนั้นผมจะหลีกเลี่ยงมันที่ดีที่สุดคือเพียงif(pointer = NULL)
(ฉันยังแนะนำเงื่อนไขบางอย่างของโยดาในคำตอบเดียวแต่นั่นเป็นเรื่องที่ต่างออกไป)if(pointer)
ในทำนองเดียวกันสำหรับwhile (node != NULL && node->data == key)
ฉันฉันจะเขียนwhile (node && node->data == key)
ที่ชัดเจนมากขึ้นสำหรับฉัน (แสดงว่าใช้ไฟฟ้าลัดวงจร)
(boolean expression)? true : false
ไม่มีจุดหมายอย่างสมบูรณ์ ประเมินการแสดงออกอย่างใดอย่างหนึ่งtrue
หรือfalse
; สิ่งที่คุณพูดคือ "ถ้าเป็นจริงให้ฉันจริงถ้ามันเป็นเท็จให้ฉันเป็นเท็จ" กล่าวโดยย่อ: มันเทียบเท่ากับการแสดงออกทางบูลีนอย่างสมบูรณ์ โปรดทราบว่าnode == NULL
นี่เป็นนิพจน์บูลีน BTW การใช้งานสองอย่างของคุณจะกลับมาตรงกันข้ามกัน ไม่ว่าคุณต้องการ!=
ในครั้งแรกหรือเพียงหนึ่ง!
ในสอง
=
แทน==
การทำตัวแปรของคุณconst
ทุกครั้งที่ทำได้ ตัวอย่างเช่นคุณสามารถกำหนดฟังก์ชั่นของคุณเป็นisEmnpy(node* const head) { ... }
แล้วคอมไพเลอร์จะปฏิเสธที่จะรวบรวมมันถ้าคุณตั้งใจเขียนแทนnode = NULL
node == NULL
แน่นอนว่าใช้ได้กับตัวแปรที่คุณไม่จำเป็นต้องเปลี่ยนเท่านั้น
T* get() const
แทนที่จะoperator T*() const
หลีกเลี่ยงการแปลงโดยนัย operator bool() const
แต่พวกเขาไม่ได้มี
การตรวจสอบอย่างชัดเจนสำหรับ NULL สามารถให้คำแนะนำแก่คอมไพเลอร์เกี่ยวกับสิ่งที่คุณพยายามทำซึ่งจะนำไปสู่ข้อผิดพลาดน้อย
ใช่คุณสามารถ. ความสามารถในการเปรียบเทียบค่ากับศูนย์โดยนัยได้รับการสืบทอดมาจาก C และมีอยู่ใน C ++ ทุกเวอร์ชัน คุณยังสามารถใช้if (!pointer)
เพื่อตรวจสอบพอยน์เตอร์สำหรับ NULL
กรณีการใช้งานที่เกี่ยวข้องสำหรับพอยน์เตอร์พอยน์เตอร์คือ
ไดนามิกดุจ การชี้ตัวชี้ระดับพื้นฐานไปยังคลาสที่ได้รับมาหนึ่ง ๆ (สิ่งที่คุณควรพยายามหลีกเลี่ยงอีกครั้ง แต่ในบางครั้งอาจพบว่าจำเป็น) ประสบความสำเร็จเสมอ แต่ผลลัพธ์ในตัวชี้โมฆะถ้าคลาสที่ได้รับไม่ตรงกัน วิธีหนึ่งในการตรวจสอบนี้คือ
Derived* derived_ptr = dynamic_cast<Derived*>(base_ptr);
if(derived_ptr != nullptr) { ... }
(หรือดีกว่าauto derived_ptr = ...
) ตอนนี้สิ่งนี้ไม่ดีเพราะมันปล่อยให้ตัวชี้ที่ได้รับ (อาจไม่ถูกต้องเช่นว่าง) อยู่นอกif
ขอบเขตของบล็อกความปลอดภัย สิ่งนี้ไม่จำเป็นเนื่องจาก C ++ ช่วยให้คุณสามารถแนะนำตัวแปรแบบบูลที่สามารถแปลงได้ภายในif
-condition :
if(auto derived_ptr = dynamic_cast<Derived*>(base_ptr)) { ... }
ซึ่งไม่เพียง แต่สั้นและปลอดภัยเท่านั้น แต่ยังมีความชัดเจนมากขึ้นด้วย: เมื่อคุณตรวจสอบ null ในเงื่อนไข if-if ที่แยกต่างหากผู้อ่านสงสัยว่า "ตกลงดังนั้นderived_ptr
จะต้องไม่เป็นโมฆะที่นี่ ... ทำไมจะเป็นเช่นนั้น มันจะว่างเปล่า " ในขณะที่รุ่นหนึ่งบรรทัดพูดชัดถ้อยชัดคำมาก "ถ้าคุณสามารถโยนได้อย่างปลอดภัยbase_ptr
ไปDerived*
แล้วใช้งานได้ ..."
การทำงานแบบเดียวกันเช่นเดียวกับการดำเนินการที่ล้มเหลวอื่น ๆ ที่ส่งกลับตัวชี้ถึงแม้ว่าโดยทั่วไปแล้ว IMO คุณควรหลีกเลี่ยงสิ่งนี้: ควรใช้บางอย่างเช่นboost::optional
"คอนเทนเนอร์" เพื่อผลลัพธ์ของการดำเนินการที่อาจล้มเหลวแทนที่จะเป็นพอยน์เตอร์
ดังนั้นถ้ากรณีที่ใช้หลักสำหรับตัวชี้ null ควรจะเขียนในรูปแบบของนัยหล่อสไตล์ผมว่ามันเป็นเรื่องดีสำหรับเหตุผลที่สอดคล้องไปเสมอใช้รูปแบบนี้คือผมจะสนับสนุนให้มากกว่าif(ptr)
if(ptr!=nullptr)
ฉันกลัวฉันจะต้องจบลงด้วยการอ้างอิงที่: if(auto bla = ...)
ไวยากรณ์เป็นจริงเพียงประมาณยุ่งยากเล็กน้อยไปที่จริงวิธีการแก้ปัญหาดังกล่าว: จับคู่รูปแบบ ทำไมคุณต้องบังคับการกระทำบางอย่าง (เช่นการชี้ตัวชี้) แล้วพิจารณาว่าอาจมีความล้มเหลว ... ฉันหมายถึงมันไร้สาระใช่มั้ย มันเหมือนกับว่าคุณมีอาหารและต้องการทำซุป คุณมอบมันให้กับผู้ช่วยของคุณพร้อมกับภารกิจในการสกัดน้ำผลไม้หากมันเป็นผักที่อ่อนนุ่ม คุณไม่ได้ดูมันก่อน เมื่อคุณมีมันฝรั่งคุณยังมอบมันให้กับผู้ช่วยของคุณ แต่พวกมันตบหน้ามันด้วยโน้ตล้มเหลว Ah, การเขียนโปรแกรมที่จำเป็น!
ดีกว่ามาก: ลองพิจารณาทุกกรณีที่คุณพบ จากนั้นปฏิบัติตาม Haskell:
makeSoupOf :: Foodstuff -> Liquid
makeSoupOf p@(Potato{..}) = mash (boil p) <> water
makeSoupOf vegetable
| isSoft vegetable = squeeze vegetable <> salt
makeSoupOf stuff = boil (throwIn (water<>salt) stuff)
Haskell ยังมีเครื่องมือพิเศษสำหรับเมื่อมีความเป็นไปได้ที่ร้ายแรงของความล้มเหลว (เช่นเดียวกับสิ่งอื่น ๆ ทั้งหมด): monads แต่นี่ไม่ใช่สถานที่สำหรับอธิบาย
⟨ / advert⟩
if(ptr)
มากกว่าที่if(ptr != nullptr)
จะพูดมากกว่านี้
ใช่แน่นอน! ในความเป็นจริงการเขียนถ้า (ตัวชี้) เป็นวิธีที่สะดวกกว่าในการเขียนมากกว่า (ตัวชี้! = NULL) เพราะ: 1. ง่ายต่อการดีบัก 2. ง่ายต่อการเข้าใจ 3. ถ้าตั้งใจค่า NULL จะถูกกำหนด จากนั้นรหัสก็จะไม่ผิดพลาด
ใช่. ในความเป็นจริงคุณควร หากคุณสงสัยว่ามันสร้างความผิดพลาดในการแบ่งเซ็กเมนต์หรือไม่
ในขณะที่คนอื่นตอบแล้วดีพวกเขาทั้งคู่จะใช้แทนกันได้
pointer != NULL
กระนั้นก็มูลค่าการกล่าวขวัญว่าอาจมีกรณีที่คุณอาจต้องการที่จะใช้คำสั่งอย่างชัดเจนคือ
ดูเพิ่มเติมที่https://stackoverflow.com/a/60891279/2463963
ได้คุณสามารถทำเช่นนี้ได้เสมอเมื่อเงื่อนไข 'IF' ประเมินเฉพาะเมื่อเงื่อนไขภายในเป็นจริง C ไม่มีประเภทส่งคืนบูลีนดังนั้นจึงส่งคืนค่าที่ไม่เป็นศูนย์เมื่อเงื่อนไขเป็นจริงในขณะที่ส่งคืน 0 เมื่อใดก็ตามที่เงื่อนไขใน 'IF' กลายเป็นเท็จ ค่าที่ไม่ใช่ศูนย์ที่ส่งคืนโดยค่าเริ่มต้นคือ 1 ดังนั้นทั้งสองวิธีในการเขียนรหัสนั้นถูกต้องในขณะที่ฉันจะชอบค่าที่สองเสมอ
ฉันคิดว่าเป็นกฎง่ายๆหากการแสดงออกของคุณสามารถเขียนใหม่เป็น
const bool local_predicate = *if-expression*;
if (local_predicate) ...
ดังกล่าวที่จะทำให้คำเตือนไม่มีแล้วที่ควรจะเป็นรูปแบบที่ต้องการสำหรับถ้าแสดงออก (ฉันรู้ว่าฉันได้รับคำเตือนเมื่อฉันกำหนด C BOOL
( #define BOOL int
) เก่าแก่C ++ bool
ให้ชี้อย่างเดียว)
"ปลอดภัยหรือไม่ .. " เป็นคำถามเกี่ยวกับมาตรฐานภาษาและรหัสที่สร้างขึ้น
"เป็นวิธีปฏิบัติที่ดีหรือไม่?" เป็นคำถามเกี่ยวกับความเข้าใจของถ้อยแถลงที่ผู้อ่านโดยพลการของแถลงการณ์ หากคุณกำลังถามคำถามนี้แสดงว่ารุ่น "ปลอดภัย" ไม่ชัดเจนสำหรับผู้อ่านและนักเขียนในอนาคต
0
nullptr
(NULL
เป็น C'ism และต้องมีการรวมไฟล์ส่วนหัว)