कक्षा सदस्यों के रूप में संदर्भ सदस्य चर


85

मेरे काम के स्थान पर मैं इस शैली का बड़े पैमाने पर इस्तेमाल करता हूँ: -

#include <iostream>

using namespace std;

class A
{
public:
   A(int& thing) : m_thing(thing) {}
   void printit() { cout << m_thing << endl; }

protected:
   const int& m_thing; //usually would be more complex object
};


int main(int argc, char* argv[])
{
   int myint = 5;
   A myA(myint);
   myA.printit();
   return 0;
}

क्या इस मुहावरे का वर्णन करने के लिए एक नाम है? मैं यह मान रहा हूं कि एक बड़ी जटिल वस्तु को कॉपी करने के संभावित बड़े ओवरहेड को रोकना है?

क्या यह आम तौर पर अच्छा अभ्यास है? क्या इस दृष्टिकोण में कोई कमी है?


4
एक संभावित नुकसान यह है कि यदि आपके पास सदस्य चर में एक संदर्भ है वह वस्तु कहीं और नष्ट हो जाती है और आप इसे अपनी कक्षा के माध्यम से एक्सेस करने का प्रयास करते हैं
गणितज्ञ

जवाबों:


116

क्या इस मुहावरे का वर्णन करने के लिए एक नाम है?

यूएमएल में इसे एकत्रीकरण कहा जाता है। यह इस बात से अलग है कि सदस्य वस्तु संदर्भित वर्ग के स्वामित्व में नहीं है । C ++ में आप संदर्भ या संकेत के माध्यम से एकत्रीकरण को दो अलग-अलग तरीकों से लागू कर सकते हैं।

मैं यह मान रहा हूं कि एक बड़ी जटिल वस्तु को कॉपी करने के संभावित बड़े ओवरहेड को रोकना है?

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

क्या यह आम तौर पर अच्छा अभ्यास है? क्या इस दृष्टिकोण में कोई कमी है?

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


7
"नहीं, यह उपयोग करने के लिए एक बहुत बुरा कारण होगा"। क्या आप इस बिंदु पर विस्तार से बता सकते हैं? इसके बजाए क्या इस्तेमाल किया जा सकता है?
coincoin

@coin: क्या वास्तव में प्राप्त करने के लिए?
डेविड रॉड्रिग्ज - dribeas

3
to prevent the possibly large overhead of copying a big complex object?
coincoin

3
@underscore_d आपके उत्तर के लिए धन्यवाद। तब क्या होता है जब आप दोनों में से किसी का भी उपयोग नहीं कर सकते हैं। कल्पना कीजिए कि हम एक ही वस्तु को विभिन्न वर्गों के अंदर साझा करना चाहते हैं। यदि आप इस सदस्य ऑब्जेक्ट को मान से पास करते हैं तो आप प्रत्येक वर्ग के लिए ऑब्जेक्ट की एक प्रति के साथ समाप्त होते हैं? तो समाधान यह है कि प्रतिलिपि से बचने के लिए स्मार्ट पॉइंटर्स या संदर्भों का उपयोग किया जाए। नहीं ?
coincoin

2
@ plats1 यही तो मैंने लिखा है मुझे पता है कि। मेरा कहना है कि आप स्मार्ट पॉइंटर्स या संदर्भों का उपयोग कर सकते हैं।
सिक्काबैंक

37

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

विकिपीडिया पर एक दिलचस्प परिचय है ।

कांस्टीट्यूशन के लिए मैं लिखूंगा:

using T = int;

class A
{
public:
  A(const T &thing) : m_thing(thing) {}
  // ...

private:
   const T &m_thing;
};

लेकिन इस वर्ग के साथ एक समस्या यह है कि यह अस्थायी वस्तुओं के संदर्भों को स्वीकार करता है:

T t;
A a1{t};    // this is ok, but...

A a2{T()};  // ... this is BAD.

इसे जोड़ना बेहतर है (कम से कम C ++ 11 की आवश्यकता है):

class A
{
public:
  A(const T &thing) : m_thing(thing) {}
  A(const T &&) = delete;  // prevents rvalue binding
  // ...

private:
  const T &m_thing;
};

वैसे भी अगर आप कंस्ट्रक्टर को बदलते हैं:

class A
{
public:
  A(const T *thing) : m_thing(*thing) { assert(thing); }
  // ...

private:
   const T &m_thing;
};

इसकी बहुत गारंटी है कि आपके पास एक अस्थायी के लिए एक संकेतक नहीं होगा

इसके अलावा, चूंकि कंस्ट्रक्टर एक पॉइंटर लेता है, इसलिए यह उपयोगकर्ताओं के लिए स्पष्ट है Aकि उन्हें उस वस्तु के जीवनकाल पर ध्यान देने की आवश्यकता है जो वे पास करते हैं।


कुछ संबंधित विषय हैं:


20

क्या इस मुहावरे का वर्णन करने के लिए एक नाम है?

इस उपयोग का कोई नाम नहीं है, इसे "क्लास सदस्य के रूप में संदर्भ" के रूप में जाना जाता है ।

मैं यह मान रहा हूं कि एक बड़ी जटिल वस्तु को कॉपी करने के संभावित बड़े ओवरहेड को रोकना है?

हां और ऐसे परिदृश्य भी जहां आप एक वस्तु के जीवनकाल को दूसरी वस्तु के साथ जोड़ना चाहते हैं।

क्या यह आम तौर पर अच्छा अभ्यास है? क्या इस दृष्टिकोण में कोई कमी है?

आपके उपयोग पर निर्भर करता है। किसी भी भाषा की सुविधा का उपयोग करना "पाठ्यक्रमों के लिए घोड़ों को चुनना" जैसा है । यह ध्यान रखना महत्वपूर्ण है कि हर ( लगभग सभी ) भाषा सुविधा मौजूद है क्योंकि यह कुछ परिदृश्य में उपयोगी है।
वर्ग के सदस्यों के रूप में संदर्भ का उपयोग करते समय ध्यान देने योग्य कुछ महत्वपूर्ण बिंदु हैं:

  • आपको यह सुनिश्चित करने की आवश्यकता है कि संदर्भित ऑब्जेक्ट को तब तक मौजूद रहने की गारंटी है जब तक कि आपकी क्लास ऑब्जेक्ट मौजूद न हो।
  • आपको कंस्ट्रक्टर के सदस्य इनिशियलाइज़र सूची में सदस्य को इनिशियलाइज़ करना होगा। आपके पास आलसी आरंभीकरण नहीं हो सकता है, जो पॉइंटर सदस्य के मामले में संभव हो सकता है।
  • संकलक कॉपी असाइनमेंट उत्पन्न नहीं करेगा operator=()और आपको स्वयं एक प्रदान करना होगा। यह निर्धारित करना बोझिल है कि आपका =ऑपरेटर ऐसे मामले में क्या कार्रवाई करेगा। तो मूल रूप से आपकी कक्षा गैर-असाइन की जाती है
  • संदर्भ NULLकिसी अन्य वस्तु को संदर्भित करने या बनाने के लिए नहीं किया जा सकता है । यदि आपको पुनरावर्तन की आवश्यकता है, तो यह एक संदर्भ के साथ संभव नहीं है जैसा कि एक संकेतक के मामले में है।

अधिकांश व्यावहारिक उद्देश्यों के लिए (जब तक कि आप सदस्य आकार के कारण वास्तव में उच्च मेमोरी उपयोग से चिंतित नहीं हैं) केवल सूचक या संदर्भ सदस्य के बजाय एक सदस्य उदाहरण होना चाहिए। यह आपको अन्य समस्याओं के बारे में चिंता करने से बचाता है जो अतिरिक्त मेमोरी उपयोग की कीमत पर संदर्भ / पॉइंटर सदस्यों को साथ लाता है।

यदि आपको एक पॉइंटर का उपयोग करना है, तो सुनिश्चित करें कि आप कच्चे पॉइंटर के बजाय स्मार्ट पॉइंटर का उपयोग करें। यह आपके जीवन को संकेत के साथ बहुत आसान बना देगा।


" मुझे लगता है कि यह एक बड़ी जटिल वस्तु की नकल करने के संभवतः बड़े ओवरहेड को रोकने के लिए है? हां" - कृपया इस बारे में विस्तृत रूप से बताएं कि आपको लगता है कि इस पैटर्न की नकल से बचने की कोई प्रासंगिकता है, क्योंकि मैं कोई भी नहीं देख सकता। यदि यह गैर-'डिओम 'किसी भी तरह से अपने वास्तविक उद्देश्यों के साइड-इफेक्ट के रूप में किसी के लिए प्रतियां बचाता है, तो उनके मूल डिजाइन को शुरू करने के लिए गलत तरीके से त्रुटिपूर्ण किया गया था, और बस इसे इस पैटर्न के साथ जगह में बदलने की संभावना नहीं है, जो वे उम्मीद करते हैं ।
अंडरस्कोर_ड

@underscore_d कहते हैं कि एक वर्ग को गैर-तुच्छ मात्रा में डेटा की आवश्यकता होती है और एक ही समय में इस वर्ग के कई उदाहरण हो सकते हैं, यह प्रत्येक उदाहरण के लिए अस्वीकार्य रूप से व्यर्थ हो सकता है, क्योंकि यह उस कॉन्स्टेंट डेटा की अपनी प्रति है। तो उस डेटा के एक बाहरी स्थान पर एक कॉन्टेस्ट रेफरेंस को साझा किया जा सकता है जो बहुत सारी कॉपी करने से बचाता है। shared_ptr जरूरी नहीं कि एक समाधान है क्योंकि डेटा हैंडल की जरूरत नहीं है गतिशील रूप से आवंटित किया जाना चाहिए।
असमतल

@Unimportant मुझे यकीन नहीं है कि 2016 में मेरी आपत्ति क्या थी। मैं हर समय क्लास के सदस्यों के रूप में संदर्भ का उपयोग करता हूं। शायद मुझे इस बात का मलाल था कि संदर्भों के साथ केवल मूल्य-स्वामित्व की जगह जीवन भर के सवालों को जन्म देगी और जरूरी नहीं कि यह एक रामबाण या ऐसा कुछ हो जो हमेशा 1: 1 हो सकता है। मुझे नही पता।
अंडरस्कोर_ड

1

C ++ कक्षा / संरचना के निर्माण के दौरान किसी वस्तु के जीवन काल का प्रबंधन करने के लिए एक अच्छा तंत्र प्रदान करता है। यह अन्य भाषाओं में C ++ की सर्वश्रेष्ठ विशेषताओं में से एक है।

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


2
लेकिन ओपी में कोड किसी भी सदस्य चर का संदर्भ नहीं रखता है। इसका एक सदस्य चर है जो एक संदर्भ है। तो, महान अंक, लेकिन प्रतीत होता है असंबंधित।
अंडरस्कोर_ड

-3

सदस्य संदर्भों को आमतौर पर बुरा माना जाता है। वे सदस्य बिंदुओं की तुलना में जीवन को कठिन बनाते हैं। लेकिन यह विशेष रूप से असत्य नहीं है, न ही यह कुछ विशेष नाम है मुहावरा या बात है। यह सिर्फ अलियासिंग है।


23
क्या आप समर्थन के संदर्भ प्रदान कर सकते हैं जिसे आमतौर पर बुरा माना जाता है ?
डेविड रॉड्रिग्ज - dribeas
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.