নাল্পটার ব্যবহার করার সুবিধা কী কী?


163

এই কোডের টুকরোটি তিনটি পয়েন্টার (নিরাপদ পয়েন্টার সূচনা) এর জন্য ধারণাগতভাবে একই কাজ করে:

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

এবং সুতরাং, পয়েন্টারগুলি nullptrতাদের মূল্য নির্ধারণের উপর নির্ধারিত করার সুবিধা NULLবা 0কী?


39
এক জিনিস, একটি ওভারলোড ফাংশন গ্রহণ intএবং void *পছন্দ করে নিন করা হবে না intউপর সংস্করণ void *ব্যবহার করে যখন সংস্করণ nullptr
ক্রিস

2
ভাল f(nullptr)থেকে পৃথক f(NULL)। তবে যতক্ষণ না উপরের কোডটি সম্পর্কিত (স্থানীয় ভেরিয়েবলের জন্য নির্ধারিত), তিনটি পয়েন্টারই হুবহু এক। একমাত্র সুবিধা হ'ল কোড পঠনযোগ্যতা।
বালকি

2
আমি এটি @ এফএকিউ তৈরির পক্ষে, @ প্রসূন। ধন্যবাদ!
এসবিআই

1
NB NULL historতিহাসিকভাবে 0 হওয়ার গ্যারান্টিযুক্ত নয়, তবে এটি oc C99 এর মতোই, একইভাবে বাইটটি 8 বিট দীর্ঘ এবং সত্য এবং মিথ্যা নয় আর্কিটেকচার নির্ভর মান ছিল। এই প্রশ্নটি nullptrNULL
বুথের

উত্তর:


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
: @MarkGarcia এই সহায়ক হতে পারে stackoverflow.com/questions/13665349/...
ক্রিস

9
আপনার পাদটীকা পিছন দিকে মনে হয়। NULLঅবিচ্ছেদ্য প্রকারের জন্য মানক দ্বারা প্রয়োজনীয় হয় এবং এজন্যই এটি সাধারণত 0বা হিসাবে সংজ্ঞায়িত হয় 0L। এছাড়াও আমি নিশ্চিত না যে আমি nullptr_tওভারলোডটি পছন্দ করি , যেহেতু এটি কেবল সাথে কল করে nullptr, যেমন কোনও ভিন্ন ধরণের নাল পয়েন্টার সহ নয় (void*)0। তবে আমি বিশ্বাস করতে পারি যে এর কিছু ব্যবহার রয়েছে, এমনকি যদি এটি সমস্ত কিছু করে তবে আপনি নিজের কোনও একক-মূল্যবান স্থানধারক ধরণের সংজ্ঞাটি "কোনও কিছুই নয়" হিসাবে সংরক্ষণ করেন।
স্টিভ জেসোপ

1
আরেকটি সুবিধা (যদিও স্বীকারোক্তি হিসাবে নাবালক) হতে পারে nullptrএটির একটি ভাল সংজ্ঞা দেওয়া সংখ্যাসূচক মান থাকতে পারে যেখানে নাল পয়েন্টার ধ্রুবকগুলি থাকে না। একটি নাল পয়েন্টার ধ্রুবকটি সেই ধরণের নাল পয়েন্টারে রূপান্তরিত হয় (যাই হোক না কেন)। এটি একই ধরণের দুটি নাল পয়েন্টার সমানভাবে তুলনা করা প্রয়োজন এবং বুলিয়ান রূপান্তর একটি নাল পয়েন্টারকে রূপান্তরিত করে falseঅন্য কিছু প্রয়োজন হয় না। অতএব, কোনও সংকলক (মূর্খ, তবে সম্ভব) পক্ষে 0xabcdef1234নাল পয়েন্টারের জন্য উদাহরণস্বরূপ বা অন্য কোনও সংখ্যা ব্যবহার করা সম্ভব । অন্যদিকে, nullptrসংখ্যার শূন্যে রূপান্তর করা প্রয়োজন।
দামন

1
@ ডেড এমজি: আমার উত্তরে কী ভুল? যে f(nullptr)অভিযুক্ত ফাংশন কল করবে না? একাধিক প্রেরণা ছিল। প্রোগ্রামাররা আসন্ন বছরগুলিতে আরও অনেক দরকারী জিনিস আবিষ্কার করতে পারেন। সুতরাং আপনি বলতে পারবেন না যে এর একমাত্র সত্য ব্যবহার আছে nullptr
নওয়াজ

87

সি ++ ১১ টি পরিচয় করিয়ে দেয় nullptr, এটি Nullপয়েন্টার ধ্রুবক হিসাবে পরিচিত এবং এটি প্রকারের সুরক্ষার উন্নতি করে এবং বিদ্যমান বাস্তবায়ন নির্ভর নাল পয়েন্টার ধ্রুবকের বিপরীতে অস্পষ্ট পরিস্থিতি সমাধান করেNULL । এর সুবিধাগুলি বুঝতে সক্ষম হওয়া nullptr। আমাদের প্রথমে এটি বুঝতে হবে NULLযে এর সাথে জড়িত সমস্যাগুলি কী এবং কী কী।


NULLঠিক কি ?

প্রাক সি ++ 11 NULLএমন কোনও পয়েন্টারটির প্রতিনিধিত্ব করতে ব্যবহৃত হয়েছিল যার কোনও মান বা পয়েন্টার নেই যা বৈধ কিছুতে নির্দেশ করে না। জনপ্রিয় ধারণার বিপরীতে NULLসি ++ তে কোনও কীওয়ার্ড নয় । এটি স্ট্যান্ডার্ড লাইব্রেরির শিরোনামগুলিতে সংজ্ঞায়িত একটি শনাক্তকারী। সংক্ষেপে আপনি 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

সি ++ স্ট্যান্ডার্ড NUL কে নির্দিষ্ট মানক লাইব্রেরির শিরোলেখ ফাইলগুলিতে সংজ্ঞায়িত একটি বাস্তবায়ন সংজ্ঞায়িত ম্যাক্রো হিসাবে সংজ্ঞায়িত করে। NULL এর উত্স সি থেকে এবং সি ++ সি থেকে উত্তরাধিকার সূত্রে সি স্ট্যান্ডার্ডটি NULL হিসাবে 0বা হিসাবে নির্ধারিত হয়েছে (void *)0। তবে সি ++ এ একটি সূক্ষ্ম পার্থক্য রয়েছে।

সি ++ এই স্পেসিফিকেশনটি যেমন আছে তেমন মানতে পারেনি। সি থেকে পৃথক, সি ++ একটি দৃ a়ভাবে টাইপিত ভাষা (সি- void*তে কোনও প্রকারের থেকে স্পষ্ট কাস্টের প্রয়োজন হয় না , যখন সি ++ একটি সুস্পষ্ট কাস্টকে নির্দেশ দেয়)। এটি অনেক সি ++ এক্সপ্রেশনগুলিতে সি স্ট্যান্ডার্ড অকেজো দ্বারা নির্ধারিত NULL এর সংজ্ঞা তৈরি করে। উদাহরণ স্বরূপ:

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 *পয়েন্টার থেকে প্যাসিটার থেকে সদস্য কার্যের প্রয়োজন হয়।

সুতরাং সি থেকে পৃথক, সি ++ স্ট্যান্ডার্ড বাধ্যতামূলকভাবে NUL সংখ্যার আক্ষরিক 0বা হিসাবে সংজ্ঞায়িত করতে হবে 0L


সুতরাং আমরা NULLইতিমধ্যে যখন অন্য নাল পয়েন্টার ধ্রুবক প্রয়োজন ?

যদিও সি ++ স্ট্যান্ডার্ড কমিটি একটি নল সংজ্ঞা নিয়ে আসে যা সি ++ এর জন্য কাজ করবে, এই সংজ্ঞাটির নিজস্ব ন্যায্য অংশ ছিল। 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সংস্করণ বলে gets এটি কারণ NUL একটি সংখ্যার আক্ষরিক।

অধিকন্তু, যেহেতু এটি এনইউএল 0 বা 0 এল কিনা বাস্তবায়ন-সংজ্ঞায়িত তাই ফাংশন ওভারলোড রেজোলিউশনে অনেক বিভ্রান্তি থাকতে পারে।

নমুনা প্রোগ্রাম:

#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, কলটি অস্পষ্ট এবং সংকলনের ত্রুটির ফলস্বরূপ।

সুতরাং, বাস্তবায়নের উপর নির্ভর করে, একই কোডটি বিভিন্ন ফলাফল দিতে পারে, যা পরিষ্কারভাবে অনাকাঙ্ক্ষিত। স্বাভাবিকভাবেই, সি ++ স্ট্যান্ডার্ড কমিটি এটিকে সংশোধন করতে চেয়েছিল এবং এটি নাল্প্ট্রারের প্রধান প্রেরণা।


তাহলে কী nullptrএবং কীভাবে এটির সমস্যাগুলি এড়ানো যায় NULL?

সি ++ 11 nullptrনাল পয়েন্টার ধ্রুবক হিসাবে পরিবেশন করতে একটি নতুন কীওয়ার্ড প্রবর্তন করে । NULL এর বিপরীতে, এর আচরণ বাস্তবায়ন-সংজ্ঞায়িত নয়। এটি ম্যাক্রো নয় তবে এটির নিজস্ব ধরণ রয়েছে। নালপ্টারের টাইপ রয়েছে std::nullptr_t। সি ++ 11 সঠিকভাবে NULL এর অসুবিধাগুলি এড়াতে নাল্প্টারের জন্য বৈশিষ্ট্যগুলি সংজ্ঞায়িত করে। এর বৈশিষ্ট্যগুলির সংক্ষিপ্তসার হিসাবে:

সম্পত্তি 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 এবং 3

সুতরাং নালপ্টারের পরিচয় ভাল পুরাতন NUL এর সমস্ত সমস্যা এড়ানো হয়।

আপনার কোথায় এবং কোথায় ব্যবহার করা উচিত nullptr?

সি ++ 11 এর জন্য থাম্বের নিয়মটি nullptrযখনই আপনি অন্যথায় NULL অতীতে ব্যবহার করতেন কেবল তখনই ব্যবহার শুরু হয় ।


স্ট্যান্ডার্ড তথ্যসূত্র:

সি ++ ১১ টি স্ট্যান্ডার্ড: C.3.2.4 ম্যাক্রো নুল
সি ++ 11 স্ট্যান্ডার্ড: 18.2 প্রকার
সি ++ 11 স্ট্যান্ডার্ড: 4.10 পয়েন্টার রূপান্তর
C99 স্ট্যান্ডার্ড: 6.3.2.3 পয়েন্টার


আমি জানার পর থেকেই আমি আপনার শেষ পরামর্শটি অনুশীলন করছি nullptr, যদিও আমার কোডের সাথে এটি আসলে কী পার্থক্য তা আমি জানতাম না। দুর্দান্ত উত্তরের জন্য এবং বিশেষত প্রচেষ্টার জন্য ধন্যবাদ। বিষয়টি নিয়ে আমাকে একটি দুর্দান্ত আলো এনেছে।
মার্ক গার্সিয়া

"নির্দিষ্ট স্ট্যান্ডার্ড লাইব্রেরির শিরোনাম ফাইলগুলিতে।" -> প্রথম থেকেই কেন "সিএসডিডিএফ" লিখবেন না?
mxMLnkn

কেন আমরা নালপ্টরকে বুল ধরণের রূপান্তরিত হতে দেব? আপনি আরও বিস্তারিত বলতে পারেন?
রবার্ট ওয়াং

... কোনও পয়েন্টারের প্রতিনিধিত্ব করতে ব্যবহৃত হয়েছিল যার কোনও মূল্য নেই ... চলকগুলির সর্বদা একটি মান থাকে। এটি শব্দ হতে পারে, বা 0xccccc...., তবে, একটি মান-কম ভেরিয়েবল একটি অন্তর্নিহিত বৈপরীত্য।
ডিভে

"কেস 3: ঠিক আছে - সম্পত্তি 3" (লাইন bool flag = nullptr;)। না, ঠিক আছে না, আমি g ++ 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 একটি বিশেষ মান , তবে মানগুলি সিস্টেমের মাধ্যমে প্রচার করতে পারে না only কেবলমাত্র প্রকারগুলিই পারে। ফরওয়ার্ডিং ফাংশন অপরিহার্য, এবং 0 তাদের সাথে ডিল করতে পারে না। সুতরাং, এটি প্রবর্তন করা একেবারে প্রয়োজনীয় ছিল nullptr, যেখানে প্রকারটি বিশেষ কী এবং প্রকারটি প্রকৃতপক্ষে প্রচার করতে পারে। প্রকৃতপক্ষে, এমএসভিসি টিমকে nullptrযথাযথ রেফারেন্সগুলি কার্যকর করার পরে তফসিলের আগে তাদের পরিচয় করিয়ে দিতে হয়েছিল এবং তারপরে তাদের জন্য এই সমস্যাটি আবিষ্কার করেছিল।

আরও কয়েকটি কর্নারের কেস রয়েছে যেখানে nullptrজীবনকে সহজ করে তুলতে পারে- তবে এটি কোনও মূল ক্ষেত্রে নয়, কারণ একটি নক্ষত্র এই সমস্যাগুলি সমাধান করতে পারে। বিবেচনা

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

দুটি পৃথক ওভারলোড বোঝায়। এছাড়াও, বিবেচনা করুন

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

এটা অস্পষ্ট। তবে, নাল্প্ট্রারের সাহায্যে আপনি সরবরাহ করতে পারেন

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

7
হাস্যকর. উত্তর অর্ধেক অন্যান্য দুটি উত্তর মত যা আপনার মতে "বেশ ভুল" উত্তর !!!
নওয়াজ

ফরোয়ার্ডিং সমস্যাটি একটি কাস্ট দিয়েও সমাধান করা যায়। forward((int*)0)কাজ করে। আমি কিছু অনুপস্থিত করছি?
jcsahnwaldt মনিকা পুনরায় ইনস্টল

5

নাল্পটারের মূল কথা

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


4

nullptrআপনি উদাহরণগুলি যেভাবে দেখিয়েছেন তার কোনও সরাসরি সুবিধা নেই ।
তবে এমন পরিস্থিতিতে বিবেচনা করুন যেখানে আপনার একই নামে 2 টি কার্য রয়েছে; 1 নেয় intএবং অন্য একটিint*

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

আপনি যদি foo(int*)কোনও NUL পাস করে কল করতে চান তবে উপায়টি হ'ল:

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

nullptrএটিকে আরও সহজ এবং স্বজ্ঞাত করে তোলে :

foo(nullptr);

বাজার্নের ওয়েবপৃষ্ঠা থেকে অতিরিক্ত লিঙ্ক
অপ্রাসঙ্গিক তবে সি ++ 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
@ ডিড এমএমজি: তবে আপনি বলেছিলেন যে এই উত্তরটি "বেশ ভুল" কারণ এটি আপনার উত্তরে "সত্য প্রেরণা" সম্পর্কে কথা বলে না। এই উত্তরটি (এবং আমারও পাশাপাশি) আপনার কাছ থেকে একটি ডাউনওয়েট পেয়েছে কেবল তা নয়।
নওয়াজ

4

অন্যরা যেমন ইতিমধ্যে বলেছে, এর প্রাথমিক সুবিধা ওভারলোডের মধ্যে রয়েছে। এবং যখন সুস্পষ্ট intবনাম পয়েন্টার ওভারলোডগুলি বিরল হতে পারে তবে আদর্শ লাইব্রেরি ফাংশনগুলি বিবেচনা করুন std::fill(যা আমাকে সি ++ 03 তে একাধিকবার কামড় দিয়েছে):

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

কম্পাইল নেই: Cannot convert int to MyClass*


2

এই ওভারলোড সমস্যাগুলির চেয়ে আইএমও আরও গুরুত্বপূর্ণ: গভীরভাবে নেস্টেড টেম্পলেট তৈরির ক্ষেত্রে, ধরণের ট্র্যাকগুলি হারাতে অসুবিধা নয় এবং স্পষ্টত স্বাক্ষর দেওয়া বেশ প্রচেষ্টা an সুতরাং আপনি যে সমস্ত কিছু ব্যবহার করেন তার জন্য, উদ্দেশ্য সম্পর্কে আরও সুনির্দিষ্টভাবে মনোনিবেশ করা তত ভাল, এটি সুস্পষ্ট স্বাক্ষরগুলির প্রয়োজনীয়তা হ্রাস করবে এবং কোনও কিছু ভুল হয়ে গেলে সংকলকটিকে আরও অন্তর্দৃষ্টিযুক্ত ত্রুটি বার্তা তৈরি করতে দেয়।

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.