नल्ट्रैप की मूल बातें
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>
एक प्रकार की त्रुटि है। lockAndCall
0 के साथ कॉल विफल हो जाता है क्योंकि टेम्पलेट के अंदर, एक इंट को एक फ़ंक्शन को पास किया जा रहा है जिसके लिए एक की आवश्यकता है 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
।
int
और उपयोग करते समय संस्करण पर संस्करण काvoid *
चयन नहीं करेगा ।int
void *
nullptr