ฉันสามารถใช้ if (ตัวชี้) แทน if (ตัวชี้! = NULL) หรือไม่


171

การตรวจสอบตัวชี้ไม่ปลอดภัยNULLโดยการเขียนเพียงอย่างเดียวif(pointer)หรือฉันต้องใช้อย่างปลอดภัยif(pointer != NULL)หรือไม่?


13
ความจริงก็คือถ้าคุณกำลังจะใช้การตรวจสอบอย่างชัดเจนก็เพียงที่มีประสิทธิภาพ - และมักจะแนะนำ - ไปทดสอบกับหรือ0 nullptr( NULLเป็น C'ism และต้องมีการรวมไฟล์ส่วนหัว)
cHao

5
@danijar คุณสามารถใช้ nullptr ในปัจจุบัน C ++
SurvivalMachine

9
@cHao จุดประสงค์ในการ "เข้ากันได้กับ C" คืออะไร?
qdii

5
@danijar: ใช่คุณไม่ควรใช้NULLใน C ++ จากที่นี้เพราะNULLเป็นมาโครที่ขึ้นกับการใช้งานซึ่งอาจทำให้คุณมีพฤติกรรมที่ไม่ชัดเจน
Alok บันทึก

3
ในขณะนี้ไม่ใช่กรณี 'if' ให้ดูการสาธิตสดของ ideone ว่าทำไมคุณควรหลีกเลี่ยง "NULL" และ "0" สำหรับตัวชี้ใน C ++: ideone.com/tbvXNs
kfsone

คำตอบ:


197

คุณสามารถ; ตัวชี้โมฆะจะถูกแปลงเป็นบูลีนโดยปริยายในขณะที่พอยน์เตอร์ที่ไม่ใช่นัลถูกแปลงเป็นจริง จากมาตรฐาน C ++ 11 ส่วนในBoolean Conversions:

prvalue ของเลขคณิตนับ unscoped ตัวชี้หรือตัวชี้ไปยังประเภทสมาชิกสามารถแปลงเป็น prvalue boolของประเภท ค่าศูนย์ค่าตัวชี้โมฆะหรือค่าตัวชี้โมฆะสมาชิกถูกแปลงเป็น false; ค่าอื่น ๆ true จะถูกแปลงเป็น prvalue ชนิด std::nullptr_t สามารถแปลงเป็น prvalue ชนิด bool ; false คุ้มค่าที่เกิดขึ้นคือ


42

ใช่คุณทำได้

  • ตัวชี้โมฆะถูกแปลงเป็นเท็จโดยปริยาย
  • ตัวชี้ที่ไม่ใช่ null ถูกแปลงเป็นจริง

นี่เป็นส่วนหนึ่งของการแปลงมาตรฐาน C ++ ซึ่งอยู่ในมาตราการแปลงบูลีน :

§ 4.12 การแปลงบูลีน

prvalue ของเลขคณิตการแจงนับที่ไม่ จำกัด ขอบเขตตัวชี้หรือตัวชี้เป็นชนิดสมาชิกสามารถแปลงเป็น prvalue ของบูลประเภท ค่าศูนย์ค่าตัวชี้โมฆะหรือค่าตัวชี้โมฆะสมาชิกถูกแปลงเป็นเท็จ ค่าอื่นใดจะถูกแปลงเป็นจริง prvalue ของ type std :: nullptr_t สามารถแปลงเป็น prvalue ของ type bool; ค่าผลลัพธ์เป็นเท็จ


29

ใช่คุณสามารถ. ที่จริงแล้วฉันชอบที่จะใช้if(pointer)เพราะมันง่ายกว่าในการอ่านและเขียนเมื่อคุณชินกับมันแล้ว

นอกจากนี้ยังทราบว่า C ++ 11 แนะนำซึ่งเป็นที่ต้องการมากกว่าnullptrNULL


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

7
@ ฮาร์เปอร์คุณสามารถพูดได้ว่ามันเป็นรูปแบบการเข้ารหัส แต่คุณสามารถใช้ตรรกะเดียวกันกับif(som_integer)vs ได้if(some_integer != 0)เพราะจำนวนเต็มไม่ใช่บูลีนใช่ไหม? ฉันชอบที่จะหลีกเลี่ยง0หรือNULLในคำสั่ง if
Yu Hao

13
ฉันยอมรับว่ามันเป็นเพียงเรื่องของการเขียนโค้ด ฉันชอบที่จะชอบif (pointer)ตัวเองมากกว่า แต่if (ptr != nullptr)ดูเหมือนว่าถูกต้องตามกฎหมายสำหรับฉัน ในทางตรงกันข้ามถ้าผมเห็นใครบางคนในทีมของฉันที่เขียนผมจะทำให้พวกเขาเปลี่ยนไปif (some_integer) if (some_integer != 0)อย่างไรก็ตามฉันจะไม่แกล้งทำเป็นว่ามันไม่ใช่ความชอบโดยพลการในส่วนของฉัน - ฉันไม่ชอบที่จะรักษาพอยน์เตอร์และจำนวนเต็มเหมือนกัน
Joel

1
@YuHao และเนื่องจากเป็นรูปแบบโค้ดฉันจะไม่ระบุว่า "เป็นที่ต้องการ" แต่ "ฉันชอบ"
ฮาร์เปอร์

5
@ franji1 งั้นif(isReady) if(filePtr) if(serviceNo)เหรอ การตั้งชื่อตัวแปรที่ไม่ดีตามวัตถุประสงค์นั้นไม่ได้มีความหมายมากนัก อย่างไรก็ตามฉันมีจุดของคุณและเข้าใจแล้ว แต่ฉันสามารถยืนยันโดยใช้รูปแบบการเข้ารหัสของฉันเองได้ไหม
Yu Hao

13

ตอบคำถามแล้ว แต่ฉันต้องการเพิ่มคะแนน

ฉันจะชอบ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)ที่ชัดเจนมากขึ้นสำหรับฉัน (แสดงว่าใช้ไฟฟ้าลัดวงจร)

  • (อาจเป็นเหตุผลที่โง่เง่า) เนื่องจาก NULL เป็นแมโครถ้าสมมติว่าหนึ่งนิยามใหม่โดยไม่ได้ตั้งใจกับค่าอื่น ๆ

6
การใช้ = แทน == มักจะสร้างคำเตือนคอมไพเลอร์ในวันที่คนไม่ได้ใช้ถ้า (NULL == ptr)
paulm

@paulm ที่ฉันเพิ่งเพิ่มจุดนี้มันเรียกว่าเงื่อนไขโยดาบางคนไม่ชอบมันเพราะมันอ่านน้อยกว่า
Grijesh Chauhan

2
(boolean expression)? true : falseไม่มีจุดหมายอย่างสมบูรณ์ ประเมินการแสดงออกอย่างใดอย่างหนึ่งtrueหรือfalse; สิ่งที่คุณพูดคือ "ถ้าเป็นจริงให้ฉันจริงถ้ามันเป็นเท็จให้ฉันเป็นเท็จ" กล่าวโดยย่อ: มันเทียบเท่ากับการแสดงออกทางบูลีนอย่างสมบูรณ์ โปรดทราบว่าnode == NULLนี่เป็นนิพจน์บูลีน BTW การใช้งานสองอย่างของคุณจะกลับมาตรงกันข้ามกัน ไม่ว่าคุณต้องการ!=ในครั้งแรกหรือเพียงหนึ่ง!ในสอง
celtschk

BTW หนึ่งการป้องกันที่เป็นไปได้=แทน==การทำตัวแปรของคุณconstทุกครั้งที่ทำได้ ตัวอย่างเช่นคุณสามารถกำหนดฟังก์ชั่นของคุณเป็นisEmnpy(node* const head) { ... }แล้วคอมไพเลอร์จะปฏิเสธที่จะรวบรวมมันถ้าคุณตั้งใจเขียนแทนnode = NULL node == NULLแน่นอนว่าใช้ได้กับตัวแปรที่คุณไม่จำเป็นต้องเปลี่ยนเท่านั้น
celtschk

2
เพราะคลาสสมาร์ทพอยน์เตอร์นั้นT* get() constแทนที่จะoperator T*() constหลีกเลี่ยงการแปลงโดยนัย operator bool() constแต่พวกเขาไม่ได้มี
StellarVortex

13

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

ป้อนคำอธิบายรูปภาพที่นี่


8

ใช่คุณสามารถ. ความสามารถในการเปรียบเทียบค่ากับศูนย์โดยนัยได้รับการสืบทอดมาจาก C และมีอยู่ใน C ++ ทุกเวอร์ชัน คุณยังสามารถใช้if (!pointer)เพื่อตรวจสอบพอยน์เตอร์สำหรับ NULL


2

กรณีการใช้งานที่เกี่ยวข้องสำหรับพอยน์เตอร์พอยน์เตอร์คือ

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

    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⟩


1
ฉันเห็นได้แค่ประโยคเดียวในการพูดนานน่าเบื่อไม่รู้จบที่ตอบคำถามได้จริง
มาร์ควิสแห่ง Lorne

@EJP: หากคุณใช้คำถามอย่างแท้จริง (" ฉันสามารถใช้") ได้คุณจะไม่ได้รับคำตอบอย่างชัดเจนเลย (คำตอบคือ "ใช่") ฉันพยายามที่จะให้เหตุผลที่เหมาะสมว่าทำไม OP ควรใช้ในความเป็นจริงif(ptr)มากกว่าที่if(ptr != nullptr)จะพูดมากกว่านี้
leftaroundabout

1

ใช่แน่นอน! ในความเป็นจริงการเขียนถ้า (ตัวชี้) เป็นวิธีที่สะดวกกว่าในการเขียนมากกว่า (ตัวชี้! = NULL) เพราะ: 1. ง่ายต่อการดีบัก 2. ง่ายต่อการเข้าใจ 3. ถ้าตั้งใจค่า NULL จะถูกกำหนด จากนั้นรหัสก็จะไม่ผิดพลาด



0

ในขณะที่คนอื่นตอบแล้วดีพวกเขาทั้งคู่จะใช้แทนกันได้

pointer != NULLกระนั้นก็มูลค่าการกล่าวขวัญว่าอาจมีกรณีที่คุณอาจต้องการที่จะใช้คำสั่งอย่างชัดเจนคือ

ดูเพิ่มเติมที่https://stackoverflow.com/a/60891279/2463963


-1

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


1
ค่าที่ไม่ใช่ศูนย์โดยค่าเริ่มต้นจะไม่ได้กำหนดถ้าฉันจำได้อย่างถูกต้อง
มาร์ควิสแห่ง Lorne

-1

ฉันคิดว่าเป็นกฎง่ายๆหากการแสดงออกของคุณสามารถเขียนใหม่เป็น

const bool local_predicate = *if-expression*;
if (local_predicate) ...

ดังกล่าวที่จะทำให้คำเตือนไม่มีแล้วที่ควรจะเป็นรูปแบบที่ต้องการสำหรับถ้าแสดงออก (ฉันรู้ว่าฉันได้รับคำเตือนเมื่อฉันกำหนด C BOOL( #define BOOL int) เก่าแก่C ++ boolให้ชี้อย่างเดียว)


-1

"ปลอดภัยหรือไม่ .. " เป็นคำถามเกี่ยวกับมาตรฐานภาษาและรหัสที่สร้างขึ้น

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


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

@danijar คุณจำไม่ได้เมื่อคุณยังใหม่กับ StackOverflow และค้นหาส่วน 'ความคิดเห็น' โดยไม่ประสบความสำเร็จใช่ไหม คนที่มี 7 ชื่อเสียงไม่สามารถทำเช่นนั้นได้
Broxzier

@JimBalter ซึ่งสับสนมากเนื่องจากคุณสามารถเห็นคนอื่นทำเช่นนั้น เมื่อฉันยังใหม่กับใครบางคนโทษฉันเพราะทำเช่นนั้น
Broxzier

@ JimBalter ฉันไม่ได้ฆ่าและขโมย ฉันบอกdanijarว่าFred Mitchellเป็นผู้ใช้ใหม่และไม่สามารถโพสต์ความคิดเห็นได้
Broxzier

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