मापदंडों को सही ढंग से कैसे पारित करें?


108

मैं C ++ शुरुआती हूं, लेकिन प्रोग्रामिंग शुरुआत करने वाला नहीं हूं। मैं C ++ (c ++ 11) सीखने की कोशिश कर रहा हूं और यह मेरे लिए सबसे महत्वपूर्ण बात है: पासिंग पैरामीटर।

मैंने इन सरल उदाहरणों पर विचार किया:

  • एक ऐसा वर्ग जिसके सभी सदस्य आदिम प्रकार हैं:
    CreditCard(std::string number, int expMonth, int expYear,int pin):number(number), expMonth(expMonth), expYear(expYear), pin(pin)

  • एक वर्ग जिसमें सदस्य आदिम प्रकार + 1 जटिल प्रकार के होते हैं:
    Account(std::string number, float amount, CreditCard creditCard) : number(number), amount(amount), creditCard(creditCard)

  • एक वर्ग जिसमें कुछ जटिल प्रकार के सदस्य आदिम प्रकार + 1 संग्रह हैं: Client(std::string firstName, std::string lastName, std::vector<Account> accounts):firstName(firstName), lastName(lastName), accounts(accounts)

जब मैं एक खाता बनाता हूं, तो मैं यह करता हूं:

    CreditCard cc("12345",2,2015,1001);
    Account acc("asdasd",345, cc);

जाहिर है इस परिदृश्य में क्रेडिट कार्ड की दो बार नकल की जाएगी। अगर मैं उस रचनाकार को फिर से लिखूं

Account(std::string number, float amount, CreditCard& creditCard) 
    : number(number)
    , amount(amount)
    , creditCard(creditCard)

एक प्रति होगी। अगर मैं इसे फिर से लिखूं

Account(std::string number, float amount, CreditCard&& creditCard) 
    : number(number)
    , amount(amount)
    , creditCard(std::forward<CreditCard>(creditCard))

इसमें 2 मूव होंगे और कोई कॉपी नहीं होगी।

मुझे लगता है कि कभी-कभी आप कुछ पैरामीटर कॉपी करना चाहते हैं, कभी-कभी आप उस ऑब्जेक्ट को बनाते समय कॉपी नहीं करना चाहते हैं।
मैं C # से आता हूं और, संदर्भ के लिए इस्तेमाल किया जा रहा है, यह मेरे लिए थोड़ा अजीब है और मुझे लगता है कि प्रत्येक पैरामीटर के लिए 2 ओवरलोड होने चाहिए लेकिन मुझे पता है कि मैं गलत हूं।
क्या सी ++ में पैरामीटर भेजने का कोई सर्वोत्तम तरीका है क्योंकि मैं वास्तव में इसे ढूंढता हूं, आइए हम कहते हैं, तुच्छ नहीं। ऊपर दिए गए मेरे उदाहरणों को आप कैसे संभालेंगे?


9
मेटा: मैं विश्वास नहीं कर सकता कि किसी ने सिर्फ एक अच्छा C ++ प्रश्न पूछा है। +1।

23
FYI करें, std::stringएक वर्ग है, जैसे CreditCard, आदिम प्रकार नहीं।
क्रिस

7
स्ट्रिंग भ्रम के कारण, हालांकि असंबंधित, आपको पता होना चाहिए कि एक स्ट्रिंग शाब्दिक, "abc"प्रकार का नहीं है, प्रकार का std::stringनहीं है char */const char *, बल्कि टाइप का है const char[N](एन = 4 के साथ इस मामले में तीन वर्णों और एक अशक्त के कारण)। रास्ते से हटने के लिए यह एक अच्छी, आम गलत धारणा है।
क्रिस

10
@chuex: जॉन स्कीट सभी C # प्रश्नों पर है, कहीं अधिक शायद ही कभी C ++।
स्टीव जेसप

जवाबों:


158

सबसे महत्वपूर्ण सवाल FIRST:

क्या सी ++ में पैरामीटर भेजने का कोई सर्वोत्तम तरीका है क्योंकि मैं वास्तव में इसे ढूंढता हूं, आइए हम कहते हैं, तुच्छ नहीं

यदि आपके फ़ंक्शन को पास की जा रही मूल वस्तु को संशोधित करने की आवश्यकता है , ताकि कॉल रिटर्न के बाद, कॉल करने वाले को उस वस्तु में संशोधन दिखाई दे, तो आपको लैवल्यू संदर्भ से गुजरना चाहिए :

void foo(my_class& obj)
{
    // Modify obj here...
}

यदि आपके फ़ंक्शन को मूल ऑब्जेक्ट को संशोधित करने की आवश्यकता नहीं है, और इसकी एक प्रति बनाने की आवश्यकता नहीं है (दूसरे शब्दों में, इसे केवल इसकी स्थिति का निरीक्षण करने की आवश्यकता है), तो आपको lvalue संदर्भ सेconst गुजरना चाहिए :

void foo(my_class const& obj)
{
    // Observe obj here
}

यह आपको lvalues साथ समारोह दोनों फोन करने की अनुमति देगा और rvalues साथ (lvalues एक स्थिर पहचान के साथ वस्तुओं रहे हैं) (rvalues उदाहरण के लिए, कर रहे हैं temporaries , या वस्तुओं को आप कॉल के परिणाम के रूप से स्थानांतरित करने के बारे में कर रहे हैं std::move())।

कोई यह भी तर्क दे सकता है कि मौलिक प्रकारों या प्रकारों के लिए जिसके लिए प्रतिलिपि बनाना तेज है , जैसे कि int, boolया char, यदि संदर्भ को केवल मान का पालन करने की आवश्यकता है, तो संदर्भ द्वारा पारित करने की आवश्यकता नहीं है, और मूल्य से गुजरना चाहिए । यदि संदर्भ शब्दार्थ की आवश्यकता नहीं है, तो यह सही है, लेकिन क्या होगा यदि फ़ंक्शन पॉइंटर को उसी समान इनपुट ऑब्जेक्ट को कहीं स्टोर करना चाहता था, ताकि भविष्य में उस पॉइंटर के माध्यम से पढ़े जाने वाले मान संशोधनों को देखेंगे जो किसी अन्य भाग में किए गए हैं कोड? इस मामले में, संदर्भ से गुजरना सही समाधान है।

यदि आपके फ़ंक्शन को मूल ऑब्जेक्ट को संशोधित करने की आवश्यकता नहीं है, लेकिन उस ऑब्जेक्ट की एक प्रति संग्रहीत करने की आवश्यकता है ( संभवतः इनपुट में परिवर्तन किए बिना इनपुट के परिवर्तन का परिणाम वापस करने के लिए ), तो आप मूल्य से लेने पर विचार कर सकते हैं :

void foo(my_class obj) // One copy or one move here, but not working on
                       // the original object...
{
    // Working on obj...

    // Possibly move from obj if the result has to be stored somewhere...
}

उपरोक्त फ़ंक्शन को लागू करने पर परिणाम हमेशा एक प्रतिलिपि में होगा, जब लवलीनों को पास करना होगा, और एक चाल में जब प्रतिद्वंद्वियों को पास करना होगा। यदि आपके फ़ंक्शन को इस ऑब्जेक्ट को कहीं स्टोर करने की आवश्यकता है, तो आप इससे एक अतिरिक्त कदम उठा सकते हैं (उदाहरण के लिए, मामले foo()में एक सदस्य फ़ंक्शन है जिसे डेटा सदस्य में मान संग्रहीत करने की आवश्यकता है )।

मामले में चालें प्रकार की वस्तुओं के लिए महंगी होती हैंmy_class , तो आप ओवरलोडिंग पर विचार कर सकते हैं foo()और एक संस्करण प्रदान कर सकते हैं (lvalue संदर्भ को स्वीकार करते हुए const) और एक संस्करण rvalues ​​के लिए (एक संदर्भ को स्वीकार करते हुए):

// Overload for lvalues
void foo(my_class const& obj) // No copy, no move (just reference binding)
{
    my_class copyOfObj = obj; // Copy!
    // Working on copyOfObj...
}

// Overload for rvalues
void foo(my_class&& obj) // No copy, no move (just reference binding)
{
    my_class copyOfObj = std::move(obj); // Move! 
                                         // Notice, that invoking std::move() is 
                                         // necessary here, because obj is an
                                         // *lvalue*, even though its type is 
                                         // "rvalue reference to my_class".
    // Working on copyOfObj...
}

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

template<typename C>
void foo(C&& obj) // No copy, no move (just reference binding)
//       ^^^
//       Beware, this is not always an rvalue reference! This will "magically"
//       resolve into my_class& if an lvalue is passed, and my_class&& if an
//       rvalue is passed
{
    my_class copyOfObj = std::forward<C>(obj); // Copy if lvalue, move if rvalue
    // Working on copyOfObj...
}

आप स्कॉट मेयर्स की इस बात को देखकर इस डिज़ाइन के बारे में और जानना चाह सकते हैं (सिर्फ इस तथ्य को ध्यान में रखते हुए कि " यूनिवर्सल रेफ़रेंस " जो वह इस्तेमाल कर रहा है, वह गैर-मानक है)।

ध्यान रखने वाली एक बात यह है कि std::forwardआम तौर पर प्रतिद्वंद्वियों के लिए एक कदम खत्म हो जाएगा , इसलिए भले ही यह अपेक्षाकृत निर्दोष दिखता हो, एक ही वस्तु को कई बार अग्रेषित करना मुसीबतों का स्रोत हो सकता है - उदाहरण के लिए, एक ही वस्तु से दो बार बढ़ना! तो सावधान रहें कि इसे लूप में न रखें, और फ़ंक्शन कॉल में एक ही तर्क को कई बार अग्रेषित न करें:

template<typename C>
void foo(C&& obj)
{
    bar(std::forward<C>(obj), std::forward<C>(obj)); // Dangerous!
}

यह भी ध्यान दें, कि आप सामान्य रूप से टेम्पलेट-आधारित समाधान का सहारा नहीं लेते हैं जब तक कि आपके पास इसका कोई अच्छा कारण न हो, क्योंकि यह आपके कोड को पढ़ने के लिए कठिन बना देता है। आम तौर पर, आपको स्पष्टता और सरलता पर ध्यान देना चाहिए

उपरोक्त केवल सरल दिशानिर्देश हैं, लेकिन अधिकांश समय वे आपको अच्छे डिजाइन निर्णयों की ओर इंगित करेंगे।


आपके पोस्ट के संबंध का पता लगाना:

अगर मैं इसे […] के रूप में फिर से लिखूंगा तो 2 चालें होंगी और कोई कॉपी नहीं होगी।

यह सही नहीं है। इसके साथ शुरू करने के लिए, एक रेवल्यू रेफरेंस एक लैवल्यू से बंध नहीं सकता है, इसलिए यह केवल तभी संकलित होगा जब आप CreditCardअपने कंस्ट्रक्टर के लिए टाइप का एक रव्यू पास कर रहे हों । उदाहरण के लिए:

// Here you are passing a temporary (OK! temporaries are rvalues)
Account acc("asdasd",345, CreditCard("12345",2,2015,1001));

CreditCard cc("12345",2,2015,1001);
// Here you are passing the result of std::move (OK! that's also an rvalue)
Account acc("asdasd",345, std::move(cc));

यदि आप ऐसा करने की कोशिश करते हैं तो यह काम नहीं करेगा:

CreditCard cc("12345",2,2015,1001);
Account acc("asdasd",345, cc); // ERROR! cc is an lvalue

चूँकि ccएक लवलीन है और रेवले के सन्दर्भ लवलीन से बंध नहीं सकते। इसके अलावा, जब किसी ऑब्जेक्ट के संदर्भ को बाइंड किया जाता है, तो कोई भी चालन नहीं किया जाता है : यह केवल एक संदर्भ बाइंडिंग है। इस प्रकार, केवल एक चाल होगी।


इसलिए इस उत्तर के पहले भाग में दिए गए दिशा-निर्देशों के आधार पर, यदि आप एक CreditCardमूल्य लेते समय उत्पन्न होने वाली संख्या की संख्या से चिंतित हैं, तो आप दो कंस्ट्रक्टर अधिभार को परिभाषित कर सकते हैं, एक लेवल्यू संदर्भ ले रहा है const( CreditCard const&) और एक ले रहा है एक संदर्भ संदर्भ ( CreditCard&&)।

ओवरलोड रिज़ॉल्यूशन एक लैवल्यू पास करते समय पूर्व का चयन करेगा (इस मामले में, एक प्रति का प्रदर्शन किया जाएगा) और बाद में एक रिवेल्यू पास करते समय (इस मामले में, एक चाल का प्रदर्शन किया जाएगा)।

Account(std::string number, float amount, CreditCard const& creditCard) 
: number(number), amount(amount), creditCard(creditCard) // copy here
{ }

Account(std::string number, float amount, CreditCard&& creditCard) 
: number(number), amount(amount), creditCard(std::move(creditCard)) // move here
{ }

std::forward<>जब आप पूर्ण अग्रेषण प्राप्त करना चाहते हैं तो आपका उपयोग सामान्य रूप से देखा जाता है । उस स्थिति में, आपका कंस्ट्रक्टर वास्तव में एक कंस्ट्रक्टर टेम्पलेट होगा, और निम्नानुसार कम या ज्यादा दिखाई देगा

template<typename C>
Account(std::string number, float amount, C&& creditCard) 
: number(number), amount(amount), creditCard(std::forward<C>(creditCard)) { }

एक मायने में, यह जोड़ती दोनों भार के मैं एक ही समारोह में पहले से दिखाया है: Cहोने के लिए निष्कर्ष निकाला हो जाएगा CreditCard&मामले में आप एक lvalue से गुजर रहे हैं, और संदर्भ नियम टूट के कारण, यह इस समारोह instantiated हो जाएंगी:

Account(std::string number, float amount, CreditCard& creditCard) : 
number(num), amount(amount), creditCard(std::forward<CreditCard&>(creditCard)) 
{ }

जैसा कि आप चाहते हैं, यह एक कॉपी-निर्माण का कारण creditCardहोगा। दूसरी ओर, जब एक प्रतिद्वंद्विता पारित हो जाती है, Cतो होने के लिए कटौती की जाएगी CreditCard, और इस फ़ंक्शन को इसके बजाय त्वरित किया जाएगा:

Account(std::string number, float amount, CreditCard&& creditCard) : 
number(num), amount(amount), creditCard(std::forward<CreditCard>(creditCard)) 
{ }

यह एक चाल-निर्माण का कारण बनेगा creditCard, जो आप चाहते हैं (क्योंकि पारित किया जा रहा मूल्य एक प्रतिद्वंद्विता है, और इसका मतलब है कि हम इसे से स्थानांतरित करने के लिए अधिकृत हैं)।


क्या यह कहना गलत है कि गैर-आदिम पैरामीटर प्रकार के लिए हमेशा आगे के साथ टेम्पलेट संस्करण का उपयोग करना ठीक है?
जैक विल्सन

1
@JackWillson: मैं कहूंगा कि आपको केवल टेम्प्लेट संस्करण का सहारा लेना चाहिए, जब आपको चालों के प्रदर्शन पर चिंता हो। अधिक जानकारी के लिए, मेरा यह प्रश्न देखें , जिसका उत्तर अच्छा है। सामान्य तौर पर, अगर आपको एक प्रति नहीं बनानी है और केवल निरीक्षण करने की आवश्यकता है, तो रेफरी द्वारा लें const। यदि आपको मूल ऑब्जेक्ट को संशोधित करने की आवश्यकता है, तो रेफरी द्वारा गैर-ऑन्कस्ट में ले जाएं। यदि आपको एक प्रतिलिपि बनाने की आवश्यकता है और चालें सस्ती हैं, तो मूल्य से लें और फिर स्थानांतरित करें।
एंडी प्रोल

2
मुझे लगता है कि तीसरे कोड के नमूने में स्थानांतरित करने की कोई आवश्यकता नहीं है। आप सिर्फ objएक स्थानीय प्रतिलिपि बनाने के बजाय उपयोग कर सकते हैं , नहीं?
जुआनकोपंजा

3
@AndyProwl: आपको मेरा +1 घंटे पहले मिल गया था, लेकिन आपके (उत्कृष्ट) उत्तर को फिर से पढ़ना, मैं दो बातें बताना चाहता हूं: क) मूल्य हमेशा एक प्रति नहीं बनाता है (यदि इसे स्थानांतरित नहीं किया गया है)। कॉलर्स साइट पर इन-प्लेस का निर्माण किया जा सकता है, विशेष रूप से आरवीओ / एनआरवीओ के साथ यह पहले से ही अधिक बार काम करेगा। बी) कृपया ध्यान दें कि अंतिम उदाहरण में, केवल एक बार हीstd::forward बुलाया जा सकता है । मैंने लोगों को इसे लूप में डालते हुए देखा है, आदि और चूंकि यह उत्तर बहुत सारे शुरुआती लोग देखेंगे, इसलिए IMHO को एक मोटा "चेतावनी!" होना चाहिए - इस जाल से बचने में मदद करने के लिए लेबल।
डैनियल फ्रीडी

1
@SteveJessop: और इसके अलावा, आगे के कार्यों ( विशेष रूप से एक तर्क लेने वाले निर्माता ) के साथ तकनीकी मुद्दे (जरूरी नहीं कि समस्याएं) हैं , ज्यादातर यह तथ्य है कि वे किसी भी प्रकार के तर्क को स्वीकार करते हैं और std::is_constructible<>टाइप गुण को तब तक पराजित कर सकते हैं जब तक कि वे ठीक से SFINAE- विवश - जो कुछ के लिए तुच्छ नहीं हो सकता है।
एंडी प्रोल

11

पहले, मुझे कुछ विवरणों को सही करने दें। जब आप निम्नलिखित कहते हैं:

2 चालें होंगी और कोई प्रति नहीं होगी।

वह झूठा है। एक संदर्भ के संदर्भ में बांधना एक चाल नहीं है। एक ही चाल है।

इसके अतिरिक्त, चूंकि CreditCardकोई टेम्प्लेट पैरामीटर नहीं है, std::forward<CreditCard>(creditCard)केवल कहने का एक क्रियात्मक तरीका है std::move(creditCard)

अभी...

यदि आपके प्रकार में "सस्ते" चालें हैं, तो आप बस अपने जीवन को आसान बनाना चाहते हैं और हर चीज को मूल्य और " std::moveसाथ" ले सकते हैं।

Account(std::string number, float amount, CreditCard creditCard)
: number(std::move(number),
  amount(amount),
  creditCard(std::move(creditCard)) {}

यह दृष्टिकोण आपको दो चालें देगा जब यह केवल एक ही उपज सकता है, लेकिन यदि चालें सस्ती हैं, तो वे स्वीकार्य हो सकते हैं।

जब हम इस "सस्ती चाल" मामले पर होते हैं, तो मुझे आपको याद दिलाना चाहिए जो std::stringअक्सर तथाकथित छोटे स्ट्रिंग अनुकूलन के साथ लागू किया जाता है, इसलिए इसकी चाल कुछ बिंदुओं को कॉपी करने के समान सस्ता नहीं हो सकती है। अनुकूलन मुद्दों के साथ हमेशा की तरह, चाहे वह आपके प्रोफाइलर से पूछने के लिए कुछ भी हो या नहीं, मुझे नहीं।

यदि आप उन अतिरिक्त चालों को उठाना नहीं चाहते हैं तो क्या करें? हो सकता है कि वे बहुत महंगे या बदतर साबित हों, हो सकता है कि प्रकार वास्तव में स्थानांतरित नहीं किए जा सकते हैं और आप अतिरिक्त प्रतियां खरीद सकते हैं।

यदि केवल एक समस्याग्रस्त पैरामीटर है, तो आप दो अधिभार प्रदान कर सकते हैं, साथ T const&और T&&। वह वास्तविक सदस्य प्रारंभिककरण तक सभी संदर्भों को बांध देगा, जहां एक प्रतिलिपि या चाल होती है।

हालांकि, यदि आपके पास एक से अधिक पैरामीटर हैं, तो यह ओवरलोड की संख्या में एक विस्फोट विस्फोट की ओर जाता है।

यह एक ऐसी समस्या है जिसे पूर्ण अग्रेषण से हल किया जा सकता है। इसका मतलब है कि आप इसके बजाय एक टेम्पलेट लिखते हैं, और std::forwardसदस्यों के रूप में उनके अंतिम गंतव्य के लिए तर्कों के मूल्य श्रेणी के साथ ले जाने के लिए उपयोग करते हैं।

template <typename TString, typename TCreditCard>
Account(TString&& number, float amount, TCreditCard&& creditCard)
: number(std::forward<TString>(number),
  amount(amount),
  creditCard(std::forward<TCreditCard>(creditCard)) {}

टेम्पलेट संस्करण के साथ एक समस्या है: उपयोगकर्ता Account("",0,{brace, initialisation})अब और नहीं लिख सकता है।
आईपीसी

@ipc आह, सच। यह वास्तव में कष्टप्रद है, और मुझे नहीं लगता कि एक आसान मापनीय कार्यबल है।
आर। मार्टिनो फर्नांडीस

6

सबसे पहले, std::stringबिल्कुल की तरह एक भारी वर्ग प्रकार है std::vector। यह निश्चित रूप से आदिम नहीं है।

यदि आप किसी निर्माणकर्ता में किसी बड़े चल प्रकार को मान कर ले जा रहे हैं, तो मैं std::moveउन्हें सदस्य में शामिल करूंगा :

CreditCard(std::string number, float amount, CreditCard creditCard)
  : number(std::move(number)), amount(amount), creditCard(std::move(creditCard))
{ }

यह ठीक यही है कि मैं कंस्ट्रक्टर को लागू करने की सिफारिश कैसे करूंगा। यह सदस्यों का कारण बनता है numberऔर creditCardनिर्माण की जाने वाली कॉपी के बजाय निर्माण किया जाता है। जब आप इस कंस्ट्रक्टर का उपयोग करते हैं, तो एक कॉपी (या मूव, अगर अस्थायी) होगी क्योंकि ऑब्जेक्ट कंस्ट्रक्टर में पास हो जाता है और फिर सदस्य को इनिशियलाइज़ करते समय एक मूव होता है।

अब इस निर्माता पर विचार करें:

Account(std::string number, float amount, CreditCard& creditCard)
  : number(number), amount(amount), creditCard(creditCard)

आप सही कह रहे हैं, इसमें एक प्रति शामिल होगी creditCard, क्योंकि यह पहली बार संदर्भ द्वारा कंस्ट्रक्टर को दी गई है। लेकिन अब आप constकंस्ट्रक्टर को ऑब्जेक्ट पास नहीं कर सकते (क्योंकि संदर्भ गैर है const) और आप अस्थायी ऑब्जेक्ट को पास नहीं कर सकते। उदाहरण के लिए, आप ऐसा नहीं कर सकते:

Account account("something", 10.0f, CreditCard("12345",2,2015,1001));

अब विचार करते हैं:

Account(std::string number, float amount, CreditCard&& creditCard)
  : number(number), amount(amount), creditCard(std::forward<CreditCard>(creditCard))

यहाँ आप rvalue संदर्भ की एक गलतफहमी दिखाया है और std::forward। आपको केवल तभी उपयोग करना चाहिए std::forwardजब आप जिस वस्तु को अग्रेषित कर रहे हैं वह T&&कुछ घटाए गए प्रकार के रूप में घोषित किया गया हो T। यहां CreditCardकटौती नहीं की गई है (मैं मान रहा हूं), और इसलिए std::forwardत्रुटि में इसका उपयोग किया जा रहा है। को फिर से देखें सार्वभौमिक संदर्भ


1

मैं सामान्य मामले के लिए एक बहुत ही सरल नियम का उपयोग करता हूं: POD (int, bool, double, ...) के लिए कॉपी और सब कुछ ...

और कॉपी करना या न करना, विधि हस्ताक्षर द्वारा उत्तर नहीं दिया जाता है, लेकिन इससे अधिक आप पैरामाटर्स के साथ क्या करते हैं।

struct A {
  A(const std::string& aValue, const std::string& another) 
    : copiedValue(aValue), justARef(another) {}
  std::string copiedValue;
  const std::string& justARef; 
};

सूचक के लिए सटीक: मैंने लगभग कभी उनका उपयोग नहीं किया। केवल इससे अधिक लाभ यह है कि वे अशक्त हो सकते हैं, या फिर से असाइन किए जा सकते हैं।


2
"मैं सामान्य मामले के लिए एक काफी सरल नियम का उपयोग करता हूं: POD (int, bool, double, ...) और कॉन्स्टेबल के लिए कॉपी का उपयोग करें और बाकी सभी चीजों के लिए।" नंबर जस्ट नं।
जूता

यदि आप a (नो कांस्ट) का उपयोग करके किसी मूल्य को संशोधित करना चाहते हैं, तो उसे जोड़ना हो सकता है। और मैं नहीं देखता ... सादगी काफी अच्छी है। लेकिन अगर आपने ऐसा कहा ...
डेविड फ्ल्यू

कुछ बार आप संदर्भ आदिम प्रकारों द्वारा संशोधित करना चाहते हैं, और कभी-कभी आपको वस्तुओं की प्रतिलिपि बनाने की आवश्यकता होती है और कभी-कभी आपको वस्तुओं को स्थानांतरित करना पड़ता है। आप संदर्भ के आधार पर POD> मूल्य और यूडीटी> के लिए सब कुछ कम नहीं कर सकते।
जूता

ठीक है, यह वही है जो मैंने बाद में जोड़ा है। बहुत तेज़ जवाब हो सकता है।
डेविड फ्ल्यू

1

यह थोड़े अस्पष्ट है मेरे लिए सबसे महत्वपूर्ण बात है: पासिंग पैरामीटर।

  • यदि आप फ़ंक्शन / विधि के अंदर पारित चर को संशोधित करना चाहते हैं
    • आप इसे संदर्भ द्वारा पास करते हैं
    • आप इसे एक पॉइंटर के रूप में पास करते हैं (*)
  • यदि आप फ़ंक्शन / विधि के अंदर पारित मूल्य / चर को पढ़ना चाहते हैं
    • आप इसे संदर्भ द्वारा पास करते हैं
  • यदि आप फ़ंक्शन / विधि के अंदर पारित मूल्य को संशोधित करना चाहते हैं
    • आप वस्तु की नकल करके इसे सामान्य रूप से पास करते हैं (**)

(*) संकेत गतिशील रूप से आवंटित स्मृति को संदर्भित करते हैं, इसलिए जब संभव हो तो आपको बिंदुओं पर संदर्भ पसंद करना चाहिए, भले ही संदर्भ अंत में हों, आमतौर पर संकेत के रूप में लागू किया जाता है।

(**) "सामान्य रूप से" का अर्थ है कॉपी कंस्ट्रक्टर (यदि आप एक ही प्रकार के पैरामीटर का ऑब्जेक्ट पास करते हैं) या सामान्य कंस्ट्रक्टर द्वारा (यदि आप वर्ग के लिए एक संगत प्रकार पास करते हैं)। जब आप किसी ऑब्जेक्ट को पास करते हैं myMethod(std::string), उदाहरण के लिए, कॉपी कंस्ट्रक्टर का उपयोग किया जाता है यदि std::stringइसे पास किया जाता है, इसलिए आपको यह सुनिश्चित करना होगा कि कोई मौजूद है।

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