Nullptr का उपयोग करने के क्या फायदे हैं?


163

कोड का यह टुकड़ा तीन बिंदुओं (सुरक्षित सूचक आरंभीकरण) के लिए वैचारिक रूप से एक ही काम करता है:

int* p1 = nullptr;
int* p2 = NULL;
int* p3 = 0;

और इसलिए, nullptrउन्हें मान प्रदान करने से अधिक संकेत देने के क्या फायदे हैं NULLया 0?


39
एक बात के लिए, एक अतिभारित फ़ंक्शन लेने intऔर उपयोग करते समय संस्करण पर संस्करण का void *चयन नहीं करेगा । intvoid *nullptr
क्रिस

2
खैर इससे f(nullptr)अलग है f(NULL)। लेकिन जहां तक ​​उपरोक्त कोड का संबंध है (एक स्थानीय चर को निर्दिष्ट करना), तीनों संकेत बिल्कुल समान हैं। एकमात्र लाभ कोड पठनीयता है।
बाल्की

2
मैं इसे अक्सर पूछे जाने वाले प्रश्न के लिए @Prasoon बनाने के पक्ष में हूं। धन्यवाद!
sbi

1
एनबी NULL को ऐतिहासिक रूप से 0 होने की गारंटी नहीं है, लेकिन महासागर C99 के रूप में है, उसी तरह एक बाइट जरूरी नहीं कि 8 बिट लंबा और सच्चा और गलत आर्किटेक्चर पर निर्भर मान थे। यह सवाल केंद्रित पर nullptrbutthat 0 और के बीच का अंतर हैNULL
awiebe

जवाबों:


180

उस कोड में, एक फायदा नहीं लगता है। लेकिन निम्नलिखित अतिभारित कार्यों पर विचार करें:

void f(char const *ptr);
void f(int v);

f(NULL);  //which function will be called?

किस फंक्शन को कहा जाएगा? बेशक, यहाँ का इरादा कॉल करना है f(char const *), लेकिन वास्तव में f(int)कहा जाएगा! यह एक बड़ी समस्या है 1 , है ना?

तो, ऐसी समस्याओं का समाधान उपयोग करना है nullptr:

f(nullptr); //first function is called

बेशक, यह केवल फायदा नहीं है nullptr। यहाँ एक और है:

template<typename T, T *ptr>
struct something{};                     //primary template

template<>
struct something<nullptr_t, nullptr>{};  //partial specialization for nullptr

चूंकि टेम्प्लेट में, का प्रकार इस प्रकार nullptrघटाया जाता है nullptr_t, इसलिए आप इसे लिख सकते हैं:

template<typename T>
void f(T *ptr);   //function to handle non-nullptr argument

void f(nullptr_t); //an overload to handle nullptr argument!!!

1. सी ++ में, NULLके रूप में परिभाषित किया गया है #define NULL 0, इसलिए यह मूल रूप से है int, इसीलिए f(int)इसे कहा जाता है।


1
जैसा कि मेहरदाद ने कहा था, इस प्रकार के अतिभार बहुत दुर्लभ हैं। क्या इसके अन्य प्रासंगिक लाभ हैं nullptr? (नहीं। मैं मांग नहीं कर रहा हूं)
मार्क गार्सिया

2
@ मार्कारिया, यह मददगार हो सकता है: stackoverflow.com/questions/13665349/…
क्रिस करें

9
आपका फुटनोट पीछे की ओर लगता है। NULLअभिन्न प्रकार के मानक के लिए आवश्यक है, और इसीलिए इसे आमतौर पर 0या के रूप में परिभाषित किया जाता है 0L। इसके अलावा, मुझे यकीन नहीं है कि मैं उस nullptr_tअधिभार को पसंद करता हूं , क्योंकि यह केवल कॉल के साथ पकड़ता है nullptr, न कि एक अलग प्रकार के अशक्त सूचक के साथ (void*)0। लेकिन मैं यह मान सकता हूं कि इसके कुछ उपयोग हैं, भले ही यह सब आपको अपने स्वयं के एकल-मूल्यवान स्थान-धारक प्रकार को परिभाषित करने से बचाए, जिसका अर्थ "कोई नहीं" है।
स्टीव जेसोप

1
एक अन्य लाभ (हालांकि मामूली रूप से मामूली) हो सकता है nullptrजिसमें एक अच्छी तरह से परिभाषित संख्यात्मक मूल्य हो, जबकि अशक्त सूचक स्थिरांक नहीं होते हैं। एक अशक्त सूचक स्थिरांक उस प्रकार के शून्य सूचक में परिवर्तित हो जाता है (जो कुछ भी है)। यह आवश्यक है कि एक ही प्रकार के दो अशक्त बिंदु समान रूप से तुलना करते हैं, और बूलियन रूपांतरण एक नल सूचक में बदल जाता है falseऔर कुछ नहीं चाहिए। इसलिए, संकलक (मूर्खतापूर्ण, लेकिन संभव) के लिए यह संभव है कि उदाहरण के लिए 0xabcdef1234या शून्य सूचक के लिए कुछ अन्य संख्या का उपयोग करें । दूसरी ओर, nullptrसंख्यात्मक शून्य में बदलने के लिए आवश्यक है।
डेमन

1
@DeadMG: मेरे जवाब में क्या गलत है? जो f(nullptr)इच्छित कार्य को नहीं कहेगा? एक से अधिक प्रेरणाएँ थीं। आने वाले वर्षों में कई अन्य उपयोगी चीजों को प्रोग्रामर खुद खोज सकते हैं। इसलिए आप यह नहीं कह सकते कि इसका केवल एक ही सही उपयोग है nullptr
नवाज

87

C ++ 11 परिचय देता है nullptr, इसे Nullपॉइंटर स्थिरांक के रूप में जाना जाता है और यह प्रकार की सुरक्षा में सुधार करता है और मौजूदा कार्यान्वयन पर निर्भर अशक्त सूचक स्थिरांक के विपरीत अस्पष्ट स्थितियों को हल करता हैNULL । के फायदों को समझने में सक्षम होना nullptr। हमें पहले यह समझने की जरूरत है कि क्या है NULLऔर इसके साथ क्या समस्याएं हैं।


NULLवास्तव में क्या है ?

Pre C ++ 11 NULLका इस्तेमाल एक ऐसे पॉइंटर को दर्शाने के लिए किया गया था जिसका कोई मूल्य या पॉइंटर नहीं है जो किसी भी वैध चीज़ की ओर इशारा नहीं करता है। लोकप्रिय धारणा के विपरीत NULLC ++ में कोई कीवर्ड नहीं है । यह मानक लाइब्रेरी हेडर में परिभाषित एक पहचानकर्ता है। संक्षेप में आप NULLकुछ मानक लाइब्रेरी हेडर को शामिल किए बिना उपयोग नहीं कर सकते । नमूना कार्यक्रम पर विचार करें :

int main()
{ 
    int *ptr = NULL;
    return 0;
}

आउटपुट:

prog.cpp: In function 'int main()':
prog.cpp:3:16: error: 'NULL' was not declared in this scope

C ++ मानक NULL को कुछ मानक लाइब्रेरी हेडर फ़ाइलों में परिभाषित कार्यान्वयन परिभाषित मैक्रो के रूप में परिभाषित करता है। NULL की उत्पत्ति C से है और C ++ ने इसे C से विरासत में लिया है। C मानक ने NULL को 0या के रूप में परिभाषित किया है (void *)0। लेकिन C ++ में एक सूक्ष्म अंतर है।

C ++ इस विनिर्देश को स्वीकार नहीं कर सका क्योंकि यह है। C के विपरीत, C ++ एक दृढ़ता से टाइप की जाने वाली भाषा है (C void*को किसी भी प्रकार से स्पष्ट कलाकारों की आवश्यकता नहीं है , जबकि C ++ एक स्पष्ट कलाकारों को अनिवार्य करता है)। यह सी मानक द्वारा निर्दिष्ट NULL की परिभाषा को कई C ++ अभिव्यक्तियों में बेकार कर देता है। उदाहरण के लिए:

std::string * str = NULL;         //Case 1
void (A::*ptrFunc) () = &A::doSomething;
if (ptrFunc == NULL) {}           //Case 2

यदि NULL को परिभाषित किया गया है (void *)0, तो उपरोक्त में से कोई भी अभिव्यक्ति काम नहीं करेगी।

  • केस 1: संकलित नहीं करेगा क्योंकि एक स्वचालित कास्ट की आवश्यकता void *है std::string
  • केस 2: संकलित नहीं करेगा क्योंकि void *सूचक से सदस्य फ़ंक्शन तक कास्ट की आवश्यकता है।

इसलिए C के विपरीत, C ++ मानक ने NULL को संख्यात्मक शाब्दिक 0या के रूप में परिभाषित करने के लिए अनिवार्य किया है 0L


तो जब हम पहले से NULLही एक और अशक्त सूचक स्थिरांक की क्या आवश्यकता है ?

यद्यपि C ++ मानक समिति एक पूर्ण परिभाषा के साथ आई थी जो C ++ के लिए काम करेगी, इस परिभाषा में समस्याओं का अपना उचित हिस्सा था। NULL ने लगभग सभी परिदृश्यों के लिए पर्याप्त रूप से काम किया लेकिन सभी नहीं। इसने कुछ दुर्लभ परिदृश्यों के लिए आश्चर्यजनक और गलत परिणाम दिए। उदाहरण के लिए :

#include<iostream>
void doSomething(int)
{
    std::cout<<"In Int version";
}
void doSomething(char *)
{
   std::cout<<"In char* version";
}

int main()
{
    doSomething(NULL);
    return 0;
}

आउटपुट:

In Int version

स्पष्ट रूप से, इरादा संस्करण को कॉल करने के लिए लगता है जो char*तर्क के रूप में लेता है, लेकिन जैसा कि आउटपुट फ़ंक्शन को दिखाता है जो intसंस्करण लेता है उसे कहा जाता है। ऐसा इसलिए है क्योंकि NULL एक संख्यात्मक शाब्दिक है।

इसके अलावा, चूंकि यह कार्यान्वयन-परिभाषित है कि क्या NULL 0 या 0L है, इसलिए फ़ंक्शन ओवरलोड रिज़ॉल्यूशन में बहुत भ्रम हो सकता है।

नमूना कार्यक्रम:

#include <cstddef>

void doSomething(int);
void doSomething(char *);

int main()
{
  doSomething(static_cast <char *>(0));    // Case 1
  doSomething(0);                          // Case 2
  doSomething(NULL)                        // Case 3
}

उपरोक्त स्निपेट का विश्लेषण:

  • केस 1:doSomething(char *) उम्मीद के मुताबिक कॉल ।
  • केस 2: कॉल doSomething(int)लेकिन शायद char*संस्करण वांछित था क्योंकि 0आईएस एक अशक्त सूचक भी है।
  • केस 3: यदि NULLइसे परिभाषित किया जाता है 0, तो कॉल doSomething(int)तब किया जाता है , जब doSomething(char *)इसका इरादा था, शायद रनटाइम के दौरान तर्क में त्रुटि। यदि NULLइसे परिभाषित किया जाता है 0L, तो कॉल अस्पष्ट है और संकलन त्रुटि का परिणाम है।

इसलिए, कार्यान्वयन के आधार पर, एक ही कोड विभिन्न परिणाम दे सकता है, जो स्पष्ट रूप से अवांछित है। स्वाभाविक रूप से, C ++ मानक समिति इसे ठीक करना चाहती थी और यह nullptr के लिए मुख्य प्रेरणा है।


तो क्या है nullptrऔर यह कैसे की समस्याओं से बचता है NULL?

C ++ 11 nullptrनल पॉइंटर स्थिर के रूप में सेवा करने के लिए एक नया कीवर्ड प्रस्तुत करता है। NULL के विपरीत, इसका व्यवहार कार्यान्वयन-परिभाषित नहीं है। यह स्थूल नहीं है बल्कि इसका अपना प्रकार है। nullptr का प्रकार होता है std::nullptr_t। N ++ के नुकसान से बचने के लिए C ++ 11 उचित रूप से नल्ट्रप्रॉप के गुणों को परिभाषित करता है। इसके गुणों को संक्षेप में प्रस्तुत करने के लिए:

संपत्ति 1: इसका अपना प्रकार है std::nullptr_t, और
संपत्ति 2: यह अनुमानित रूप से परिवर्तनीय और किसी भी सूचक प्रकार या पॉइंटर-टू-मेंबर प्रकार के लिए तुलनीय है, लेकिन
संपत्ति 3: यह सिवाय इसके अभिन्न प्रकार के अभिन्न रूप से परिवर्तनीय या तुलनीय नहीं है bool

निम्नलिखित उदाहरण पर विचार करें:

#include<iostream>
void doSomething(int)
{
    std::cout<<"In Int version";
}
void doSomething(char *)
{
   std::cout<<"In char* version";
}

int main()
{
    char *pc = nullptr;      // Case 1
    int i = nullptr;         // Case 2
    bool flag = nullptr;     // Case 3

    doSomething(nullptr);    // Case 4
    return 0;
}

उपरोक्त कार्यक्रम में,

  • केस 1: ठीक है - संपत्ति 2
  • केस 2: ठीक नहीं है - संपत्ति 3
  • केस 3: ठीक है - संपत्ति 3
  • केस 4: कोई भ्रम नहीं - char *संस्करण 2, संपत्ति 2 और 3

इस प्रकार nullptr का परिचय अच्छे पुराने NULL की सभी समस्याओं से बचा जाता है।

आपको कैसे और कहां उपयोग करना चाहिए nullptr?

C ++ 11 के लिए अंगूठे का नियम केवल nullptrतब उपयोग करना शुरू करता है जब आप अतीत में अन्यथा NULL का उपयोग करते थे।


मानक संदर्भ:

C ++ 11 मानक: C.3.2.4 मैक्रो NULL
C ++ 11 मानक: 18.2 प्रकार
C ++ 11 मानक: 4.10 सूचक रूपांतरण
C99 मानक: 6.3.2.3 अंक


जब से मैंने जाना है nullptr, मैं पहले से ही आपकी अंतिम सलाह का अभ्यास कर रहा हूं , हालांकि मुझे नहीं पता था कि मेरे कोड से वास्तव में क्या फर्क पड़ता है। महान उत्तर के लिए धन्यवाद और विशेष रूप से प्रयास के लिए। मुझे इस विषय पर एक महान सौदा प्रकाश लाया।
मार्क गार्सिया

"कुछ मानक लाइब्रेरी हेडर फ़ाइलों में।" -> शुरुआत से ही "cstddef" क्यों नहीं लिखा?
mxmlnkn

हमें बूल प्रकार के लिए nullptr को परिवर्तनीय होने की अनुमति क्यों देनी चाहिए? क्या आप अधिक विस्तार से बता सकते हैं?
रॉबर्ट वांग

... एक सूचक का प्रतिनिधित्व करने के लिए इस्तेमाल किया गया था जिसका कोई मूल्य नहीं है ... चर हमेशा एक मूल्य होता है। यह शोर हो सकता है, या 0xccccc...., लेकिन, एक मूल्य-कम चर एक अंतर्निहित विरोधाभास है।
1

"केस 3: ठीक है - संपत्ति 3" (पंक्ति bool flag = nullptr;)। नहीं, ठीक नहीं है, मुझे जी + 6 के साथ संकलित समय पर निम्नलिखित त्रुटि मिलती है:error: converting to ‘bool’ from ‘std::nullptr_t’ requires direct-initialization [-fpermissive]
जॉर्ज

23

यहाँ असली प्रेरणा एकदम सही अग्रेषण है

विचार करें:

void f(int* p);
template<typename T> void forward(T&& t) {
    f(std::forward<T>(t));
}
int main() {
    forward(0); // FAIL
}

सीधे शब्दों में कहें, 0 एक विशेष मूल्य है , लेकिन मूल्य प्रणाली के माध्यम से प्रचार नहीं कर सकते हैं- केवल प्रकार कर सकते हैं। अग्रेषण कार्य आवश्यक हैं, और 0 उनसे निपट नहीं सकते। इस प्रकार, परिचय करना नितांत आवश्यक था nullptr, कि प्रकार क्या है जो विशेष है, और प्रकार वास्तव में प्रचारित कर सकता है। वास्तव में, MSVC टीम को nullptrनिर्धारित समय से पहले परिचय देना था, क्योंकि उन्होंने प्रतिद्वंद्वियों के संदर्भों को लागू किया था और फिर अपने लिए इस नुकसान का पता लगाया था।

कुछ अन्य कोने मामले हैं जहां nullptrजीवन को आसान बना सकते हैं- लेकिन यह एक मुख्य मामला नहीं है, क्योंकि एक कलाकार इन समस्याओं को हल कर सकता है। विचार करें

void f(int);
void f(int*);
int main() { f(0); f(nullptr); }

दो अलग-अलग अधिभार को बुलाता है। इसके अलावा, विचार करें

void f(int*);
void f(long*);
int main() { f(0); }

यह अस्पष्ट है। लेकिन, nullptr के साथ, आप प्रदान कर सकते हैं

void f(std::nullptr_t)
int main() { f(nullptr); }

7
मजेदार। उत्तर का आधा अन्य दो उत्तरों के समान है जो आपके अनुसार "काफी गलत" उत्तर है !!!
नवाज

अग्रेषण की समस्या का समाधान एक कलाकार के साथ भी किया जा सकता है। forward((int*)0)काम करता है। क्या मैं कुछ भूल रहा हूँ?
jcsahnwaldt

5

नल्ट्रैप की मूल बातें

std::nullptr_tनल पॉइंटर शाब्दिक, नल्टर का प्रकार है। यह एक प्रकार का प्रचलन / प्रकार है std::nullptr_t। किसी भी पॉइंटर प्रकार के नल सूचक से अशक्त सूचक मान के निहितार्थ रूपांतरण मौजूद हैं।

शाब्दिक 0 एक int है, एक सूचक नहीं है। यदि C ++ स्वयं को 0 पर एक संदर्भ में देखती है, जहाँ केवल एक संकेतक का उपयोग किया जा सकता है, तो यह 0 को एक अशक्त सूचक के रूप में स्पष्ट रूप से व्याख्या करेगा, लेकिन यह एक पतन स्थिति है। C ++ की प्राथमिक नीति यह है कि 0 एक int है, एक सूचक नहीं है।

फायदा 1 - पॉइंटर और इंटीग्रल टाइप्स पर ओवरलोडिंग होने पर अस्पष्टता हटा दें

C ++ 98 में, इसका मुख्य निहितार्थ यह था कि सूचक और अभिन्न प्रकारों पर ओवरलोडिंग से आश्चर्य हो सकता है। ऐसे ओवरलोड को 0 या NULL पास करना कभी पॉइंटर ओवरलोड नहीं कहलाता है:

   void fun(int); // two overloads of fun
    void fun(void*);
    fun(0); // calls f(int), not fun(void*)
    fun(NULL); // might not compile, but typically calls fun(int). Never calls fun(void*)

उस कॉल के बारे में दिलचस्प बात यह है कि स्रोत कोड के स्पष्ट अर्थ के बीच विरोधाभास है ("मैं NULL-null पॉइंटर के साथ मजेदार कह रहा हूं") और इसका वास्तविक अर्थ ("मैं किसी तरह के पूर्णांक के साथ मजाक कह रहा हूं- शून्य नहीं है" सूचक ")।

nullptr का लाभ यह है कि इसमें अभिन्न प्रकार नहीं है। Nullptr के साथ ओवरलोड फंक्शन फन को कॉल करना void * overload (यानी पॉइंटर ओवरलोड) कहता है, क्योंकि nullptr को कुछ भी इंटीग्रल नहीं देखा जा सकता है:

fun(nullptr); // calls fun(void*) overload 

0 या NULL के बजाय nullptr का उपयोग करना इस प्रकार अधिभार संकल्प आश्चर्य से बचा जाता है।

वापसी प्रकार के लिए ऑटो का उपयोग करते समय nullptrओवर का एक और लाभNULL(0)

उदाहरण के लिए, मान लें कि आपका मुकाबला कोड बेस में है:

auto result = findRecord( /* arguments */ );
if (result == 0) {
....
}

यदि आपको पता नहीं है (या आसानी से पता नहीं चल सकता है) क्या खोजा जाता है, तो यह स्पष्ट नहीं हो सकता है कि परिणाम एक सूचक प्रकार या एक अभिन्न प्रकार है। आखिरकार, 0 (किस परिणाम के खिलाफ परीक्षण किया जाता है) किसी भी तरह से जा सकता है। यदि आप निम्नलिखित को देखते हैं,

auto result = findRecord( /* arguments */ );
if (result == nullptr) {
...
}

कोई अस्पष्टता नहीं है: परिणाम एक सूचक प्रकार होना चाहिए।

फायदा ३

#include<iostream>
#include <memory>
#include <thread>
#include <mutex>
using namespace std;
int f1(std::shared_ptr<int> spw) // call these only when
{
  //do something
  return 0;
}
double f2(std::unique_ptr<int> upw) // the appropriate
{
  //do something
  return 0.0;
}
bool f3(int* pw) // mutex is locked
{

return 0;
}

std::mutex f1m, f2m, f3m; // mutexes for f1, f2, and f3
using MuxtexGuard = std::lock_guard<std::mutex>;

void lockAndCallF1()
{
        MuxtexGuard g(f1m); // lock mutex for f1
        auto result = f1(static_cast<int>(0)); // pass 0 as null ptr to f1
        cout<< result<<endl;
}

void lockAndCallF2()
{
        MuxtexGuard g(f2m); // lock mutex for f2
        auto result = f2(static_cast<int>(NULL)); // pass NULL as null ptr to f2
        cout<< result<<endl;
}
void lockAndCallF3()
{
        MuxtexGuard g(f3m); // lock mutex for f2
        auto result = f3(nullptr);// pass nullptr as null ptr to f3 
        cout<< result<<endl;
} // unlock mutex
int main()
{
        lockAndCallF1();
        lockAndCallF2();
        lockAndCallF3();
        return 0;
}

उपरोक्त कार्यक्रम को सफलतापूर्वक संकलित और निष्पादित किया गया है, लेकिन LockAndCallF1, lockAndCallF2 और lockAndCallF3 में अनावश्यक कोड है। अगर हम इन सभी के लिए टेम्पलेट लिख सकते हैं तो इस तरह से कोड लिखना अफ़सोस की बात है lockAndCallF1, lockAndCallF2 & lockAndCallF3। तो इसे टेम्पलेट के साथ सामान्यीकृत किया जा सकता है। मैंने अनावश्यक कोड के लिए lockAndCallकई परिभाषा के बजाय टेम्पलेट फ़ंक्शन लिखा है lockAndCallF1, lockAndCallF2 & lockAndCallF3

कोड नीचे के रूप में फिर से फैक्टर्ड है:

#include<iostream>
#include <memory>
#include <thread>
#include <mutex>
using namespace std;
int f1(std::shared_ptr<int> spw) // call these only when
{
  //do something
  return 0;
}
double f2(std::unique_ptr<int> upw) // the appropriate
{
  //do something
  return 0.0;
}
bool f3(int* pw) // mutex is locked
{

return 0;
}

std::mutex f1m, f2m, f3m; // mutexes for f1, f2, and f3
using MuxtexGuard = std::lock_guard<std::mutex>;

template<typename FuncType, typename MuxType, typename PtrType>
auto lockAndCall(FuncType func, MuxType& mutex, PtrType ptr) -> decltype(func(ptr))
//decltype(auto) lockAndCall(FuncType func, MuxType& mutex, PtrType ptr)
{
        MuxtexGuard g(mutex);
        return func(ptr);
}
int main()
{
        auto result1 = lockAndCall(f1, f1m, 0); //compilation failed 
        //do something
        auto result2 = lockAndCall(f2, f2m, NULL); //compilation failed
        //do something
        auto result3 = lockAndCall(f3, f3m, nullptr);
        //do something
        return 0;
}

विस्तार से विश्लेषण क्यों संकलन के लिए विफल रहा है lockAndCall(f1, f1m, 0) & lockAndCall(f3, f3m, nullptr)के लिए नहींlockAndCall(f3, f3m, nullptr)

क्यों lockAndCall(f1, f1m, 0) & lockAndCall(f3, f3m, nullptr)फेल हुआ संकलन ?

समस्या यह है कि जब 0 को LockAndCall में पारित किया जाता है, तो टेम्पलेट प्रकार की कटौती इसके प्रकार का पता लगाने के लिए होती है। 0 का प्रकार int है, इसलिए इस कॉल के तात्कालिकता के अंदर पैरामीटर ptr का प्रकार lockAndCall है। दुर्भाग्य से, इसका मतलब यह है कि लॉकएंडकाॅल के अंदर फूंकने के लिए कॉल में, एक इंट पास किया जा रहा है, और यह उस std::shared_ptr<int>पैरामीटर के साथ संगत नहीं है जो f1उम्मीद करता है। कॉल में उत्तीर्ण 0 lockAndCallको एक अशक्त सूचक का प्रतिनिधित्व करने का इरादा था, लेकिन वास्तव में जो पारित हुआ वह अंतर था। इस int को f1 के रूप में पास करने की कोशिश std::shared_ptr<int>एक प्रकार की त्रुटि है। lockAndCall0 के साथ कॉल विफल हो जाता है क्योंकि टेम्पलेट के अंदर, एक इंट को एक फ़ंक्शन को पास किया जा रहा है जिसके लिए एक की आवश्यकता है std::shared_ptr<int>

इसमें शामिल कॉल के लिए विश्लेषण NULLअनिवार्य रूप से समान है। जब NULLपास किया जाता है lockAndCall, तो पैरामीटर ptr के लिए एक अभिन्न प्रकार का कटौती की जाती है, और एक प्रकार की त्रुटि तब होती है जब ptr- int या इंट-लाइक प्रकार - को पारित किया जाता है f2, जो एक प्राप्त करने की अपेक्षा करता है std::unique_ptr<int>

इसके विपरीत, इसमें शामिल कॉल nullptrमें कोई परेशानी नहीं है। जब nullptrपास किया जाता है lockAndCall, तो होने के लिए टाइप किया ptrजाता है std::nullptr_t। जब ptrपास किया जाता है f3, तब से एक अंतर्निहित रूपांतरण होता std::nullptr_tहै int*, क्योंकि std::nullptr_tअंतर्निहित सभी सूचक प्रकारों में परिवर्तित होता है।

यह अनुशंसित है, जब भी आप एक अशक्त सूचक को संदर्भित करना चाहते हैं, तो nullptr का उपयोग करें, न कि 0 या NULL


4

nullptrउदाहरणों को दिखाने के तरीके में होने का कोई प्रत्यक्ष लाभ नहीं है ।
लेकिन ऐसी स्थिति पर विचार करें जहां आपके पास एक ही नाम के साथ 2 कार्य हैं; 1 लेता है intऔर दूसरा एint*

void foo(int);
void foo(int*);

यदि आप foo(int*)NULL को पास करके कॉल करना चाहते हैं , तो इसका तरीका है:

foo((int*)0); // note: foo(NULL) means foo(0)

nullptrइसे और अधिक आसान और सहज बनाता है :

foo(nullptr);

बज़्ने के वेबपेज से अतिरिक्त लिंक
अप्रासंगिक लेकिन C ++ 11 साइड नोट पर:

auto p = 0; // makes auto as int
auto p = nullptr; // makes auto as decltype(nullptr)

3
संदर्भ के लिए, decltype(nullptr)है std::nullptr_t
क्रिस

2
@ मर्कर्जिया, यह एक पूर्ण विकसित प्रकार है जहाँ तक मुझे पता है।
चिर

5
@ मार्कारिया, यह एक दिलचस्प सवाल है। cppreference में है typedef decltype(nullptr) nullptr_t;:। मुझे लगता है कि मैं मानक में देख सकता हूं। आह, यह पाया गया: नोट: std :: nullptr_t एक अलग प्रकार है जो न तो एक पॉइंटर प्रकार है और न ही सदस्य प्रकार के लिए एक पॉइंटर; बल्कि, इस प्रकार का एक प्रहार एक अशक्त सूचक स्थिरांक है और इसे एक अशक्त सूचक मान या अशक्त सदस्य सूचक मान में परिवर्तित किया जा सकता है।
चिर

2
@ डेडएमजी: एक से अधिक प्रेरणाएँ थीं। आने वाले वर्षों में कई अन्य उपयोगी चीजों को प्रोग्रामर खुद खोज सकते हैं। इसलिए आप यह नहीं कह सकते कि इसका केवल एक ही सही उपयोग है nullptr
नवाज

2
@DeadMG: लेकिन आपने कहा कि यह उत्तर "काफी गलत" है, क्योंकि यह "सही प्रेरणा" के बारे में बात नहीं करता है, जिसके बारे में आपने अपने उत्तर में बात की है। इतना ही नहीं इस जवाब (और मेरा भी) को आपसे एक अपमान मिला।
नवाज

4

जैसा कि दूसरों ने पहले ही कहा है, इसका प्राथमिक लाभ ओवरलोड में है। और जबकि स्पष्ट intबनाम पॉइंटर ओवरलोड दुर्लभ हो सकते हैं, मानक पुस्तकालय कार्यों पर विचार करें std::fill( जैसे जिसने मुझे C1 03 में एक से अधिक बार काट लिया है:

MyClass *arr[4];
std::fill_n(arr, 4, NULL);

संकलन नहीं है Cannot convert int to MyClass*:।


2

IMO उन अधिभार मुद्दों से अधिक महत्वपूर्ण है: गहरी नेस्टेड टेम्पलेट निर्माणों में, प्रकारों का ट्रैक खोना मुश्किल नहीं है, और स्पष्ट हस्ताक्षर देना काफी प्रयास है। तो आपके द्वारा उपयोग की जाने वाली हर चीज के लिए, अधिक सटीक रूप से इच्छित उद्देश्य पर केंद्रित, बेहतर, यह स्पष्ट हस्ताक्षरों की आवश्यकता को कम कर देगा और संकलक को कुछ गलत होने पर अधिक व्यावहारिक त्रुटि संदेश उत्पन्न करने की अनुमति देता है।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.