নাল্পটারের মূল কথা
std::nullptr_tনাল পয়েন্টার আক্ষরিক, নালপ্ট্রারের ধরণ। এটা তোলে ধরনের prvalue / rvalue হয় std::nullptr_t। নালপ্ট্রার থেকে কোনও পয়েন্টার ধরণের নাল পয়েন্টার মান পর্যন্ত অন্তর্নিহিত রূপান্তর রয়েছে।
আক্ষরিক 0 একটি ইনট, কোনও পয়েন্টার নয়। যদি সি ++ যদি নিজেকে এমন একটি প্রসঙ্গে দেখায় যেখানে কেবলমাত্র পয়েন্টার ব্যবহার করা যায় তবে এটি হতাশাজনকভাবে 0 টি নাল পয়েন্টার হিসাবে ব্যাখ্যা করবে, তবে এটি একটি ফ্যালব্যাক অবস্থান। সি ++ এর প্রাথমিক নীতিটি হ'ল 0 টি একটি ইনট, কোনও পয়েন্টার নয়।
সুবিধা 1 - পয়েন্টার এবং ইন্টিগ্রাল ধরণের ওভারলোডিংয়ের সময় অস্পষ্টতা সরিয়ে ফেলুন
সি ++৯-এ, এর প্রাথমিক ধারণাটি হ'ল পয়েন্টার এবং অবিচ্ছেদ্য ধরণের উপর ওভারলোডিং বিস্মিত হতে পারে। এই ধরনের ওভারলোডগুলিতে 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*)
এই কলটির সম্পর্কে আকর্ষণীয় বিষয় হ'ল উত্স কোডের আপাত অর্থ ("আমি নুল-নাল পয়েন্টারটির সাথে মজা করছি") এবং এর আসল অর্থ ("আমি কিছুটা পূর্ণসংখ্যার সাথে মজা বলছি - নাল নয়) পয়েন্টার ")।
নাল্প্ট্রারের সুবিধা হ'ল এতে অবিচ্ছেদ্য প্রকার নেই। নালপ্টারের সাথে ওভারলোড হওয়া ফাংশনটিকে মজাদার কলটি অকার্যকর * ওভারলোড (অর্থাত্ পয়েন্টার ওভারলোড) বলে, কারণ নালপ্টরকে অবিচ্ছেদ্য কিছু হিসাবে দেখা যায় না:
fun(nullptr); // calls fun(void*) overload
0 বা NULL এর পরিবর্তে নালপ্ট্রার ব্যবহার করে ওভারলোডের রেজোলিউশনগুলি অবাক করে দেয়।
রিটার্ন টাইপের জন্য অটো ব্যবহার করার পরে nullptrওভারের আরেকটি সুবিধাNULL(0)
উদাহরণস্বরূপ, ধরুন আপনি একটি কোড বেসে এটির মুখোমুখি হয়েছেন:
auto result = findRecord( /* arguments */ );
if (result == 0) {
....
}
যদি আপনি রেকর্ড কী ফাইন্ডারটি জানেন (বা সহজেই তা আবিষ্কার করতে না পারেন) তবে ফলাফলটি পয়েন্টার টাইপ বা ইন্টিগ্রাল টাইপ কিনা তা পরিষ্কার নয় be সর্বোপরি, 0 (কী ফলাফলের বিরুদ্ধে পরীক্ষিত হয়) যে কোনও পথে যেতে পারে। আপনি যদি নীচে দেখতে পান তবে অন্যদিকে,
auto result = findRecord( /* arguments */ );
if (result == nullptr) {
...
}
কোনও অস্পষ্টতা নেই: ফলাফল অবশ্যই পয়েন্টারের ধরণের হতে হবে।
সুবিধা 3
#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;
}
উপরের প্রোগ্রামটি সংকলন করে এবং সফলভাবে সম্পাদন করা হয়েছে তবে লকঅ্যান্ডক্যালএফ 1, লকআ্যান্ডএলক্যালএফ 2 এবং লকঅ্যান্ডক্যালএফ 3 এর রিডান্ট্যান্ট কোড রয়েছে। আমরা যদি এই সমস্তটির জন্য টেমপ্লেট লিখতে পারি তবে এই জাতীয় কোডটি লেখার জন্য দুঃখ হয় 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 টি লকএন্ডকালে পাস করা হয়, টেম্পলেট টাইপ ছাড়ের প্রকারটি খুঁজে বের করতে তার প্রকারটি বের করতে। 0 এর ধরণটি int হয়, সুতরাং এটি লকএন্ডক্যাল-এ এই কলটির ইনস্ট্যান্টেশনের ভিতরে প্যারামিটার পিটিআর টাইপ। দুর্ভাগ্যক্রমে, এর অর্থ এটি হ'ল লকএন্ডকালের অভ্যন্তরে ফান করার কলটিতে, একটি ইনট পাস হচ্ছে এবং এটি যে std::shared_ptr<int>পরামিতিটি f1প্রত্যাশা করে তার সাথে সামঞ্জস্য নয় । কলটিতে পাস করা 0 টি lockAndCallনাল পয়েন্টার উপস্থাপনের উদ্দেশ্যে করা হয়েছিল, তবে আসলে যা পাস করেছে তা হ'ল ইনট। এই std::shared_ptr<int>প্রকারটি এফ 1 এ পাস করার চেষ্টা করা এক ধরণের ত্রুটি। lockAndCall0 সহ কলটি ব্যর্থ হয় কারণ টেম্পলেটটির অভ্যন্তরে একটি ক্রিয়াকলাপের জন্য একটি প্রেরণ করা হচ্ছে যার জন্য একটি প্রয়োজন std::shared_ptr<int>।
জড়িত কলটির বিশ্লেষণ NULLমূলত একই is যখন NULLপাস করা হয় lockAndCall, তখন প্যারামিটার পিটিআর জন্য একটি অবিচ্ছেদ্য প্রকারকে অনুমিত করা হয় এবং ptr—an int বা int-like টাইপ to এ পাস করার পরে একটি টাইপ ত্রুটি ঘটে থাকে f2যা একটি পাওয়ার আশা করে std::unique_ptr<int>।
বিপরীতে, জড়িত কলটিতে nullptrকোনও সমস্যা নেই। যখন nullptrপাস করা হয় lockAndCall, এর জন্য টাইপটি ptrহ্রাস করা হয় std::nullptr_t। কখন ptrপাস করা হয় f3, সেখানে থেকে একটি অন্তর্নিহিত রূপান্তর std::nullptr_tআছে int*, কারণ std::nullptr_tস্পষ্টতই সমস্ত পয়েন্টার ধরণের রূপান্তরিত হয়।
এটি প্রস্তাবিত হয়, যখনই আপনি নাল পয়েন্টারটি উল্লেখ করতে চান, নলপ্টার ব্যবহার করুন, 0 বা নয় NULL।
intএবংvoid *পছন্দ করে নিন করা হবে নাintউপর সংস্করণvoid *ব্যবহার করে যখন সংস্করণnullptr।