c++ - एक सी++ संदर्भ चर, बुराई वापस करने का अभ्यास है?




reference (11)

"एक संदर्भ लौटना बुरा है क्योंकि, बस [जैसा कि मैं समझता हूं] इसे हटाने से चूकना आसान हो जाता है"

सच नहीं। एक संदर्भ लौटने से स्वामित्व अर्थशास्त्र का अर्थ नहीं है। यही कारण है कि आप ऐसा करते हैं:

Value& v = thing->getTheValue();

... इसका मतलब यह नहीं है कि अब आप वी द्वारा संदर्भित स्मृति का स्वामी हैं;

हालांकि, यह भयानक कोड है:

int& getTheValue()
{
   return *new int;
}

यदि आप ऐसा कुछ कर रहे हैं क्योंकि "आपको उस उदाहरण पर पॉइंटर की आवश्यकता नहीं है" तो: 1) यदि आपको संदर्भ की आवश्यकता है तो केवल पॉइंटर को हटा दें, और 2) आपको अंततः पॉइंटर की आवश्यकता होगी, क्योंकि आपको एक मैच करना होगा एक डिलीट के साथ नया, और आपको हटाने के लिए एक पॉइंटर की आवश्यकता है।

यह मुझे लगता है कि एक छोटा सा व्यक्तिपरक है; मुझे यकीन नहीं है कि राय सर्वसम्मति से होगी (मैंने बहुत सारे कोड स्निपेट देखे हैं जहां संदर्भ लौटाए गए हैं)।

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

यह मुझे चिंतित करता है, क्योंकि मैंने उदाहरणों का पालन किया है (जब तक कि मैं चीजों की कल्पना नहीं कर रहा हूं) और इसे कुछ उचित स्थानों में किया ... क्या मैंने गलत समझा है? क्या यह बुरा है? यदि हां, तो कितना बुरा?

मुझे लगता है कि पॉइंटर्स और संदर्भों के मेरे मिश्रित बैग की वजह से, मैं इस तथ्य के साथ संयुक्त हूं कि मैं सी ++ के लिए नया हूं, और जब उपयोग करना है, तो मेरे अनुप्रयोगों को स्मृति रिसाव नरक होना चाहिए ...

साथ ही, मैं समझता हूं कि स्मार्ट / साझा पॉइंटर्स का उपयोग आमतौर पर मेमोरी लीक से बचने का सबसे अच्छा तरीका माना जाता है।


Lvalue के रूप में कार्य (उर्फ, गैर-कॉन्स संदर्भों की वापसी) को C ++ से हटा दिया जाना चाहिए। यह बहुत ही अनजान है। स्कॉट मेयर्स इस व्यवहार के साथ एक मिनट () चाहता था।

min(a,b) = 0;  // What???

जो वास्तव में एक सुधार नहीं है

setmin (a, b, 0);

उत्तरार्द्ध भी अधिक समझ में आता है।

मुझे एहसास है कि सी ++ स्टाइल स्ट्रीम के लिए लैवल्यू के रूप में कार्य महत्वपूर्ण है, लेकिन यह इंगित करने लायक है कि सी ++ स्टाइल स्ट्रीम भयानक हैं। मैं अकेला ऐसा नहीं हूं जो सोचता है ... जैसा कि मुझे याद है कि अलेक्जेंड्रेस्कू के पास बेहतर काम करने के बारे में एक बड़ा लेख था, और मेरा मानना ​​है कि बूस्ट ने एक बेहतर प्रकार की सुरक्षित I / O विधि बनाने की भी कोशिश की है।


आम तौर पर, एक संदर्भ लौटने से पूरी तरह सामान्य होता है और हर समय होता है।

यदि तुम्हारा मतलब:

int& getInt() {
    int i;
    return i;  // DON'T DO THIS.
}

वह बुराई के सभी प्रकार है। ढेर-आवंटित i दूर जाऊंगा और आप कुछ भी नहीं कर रहे हैं। यह भी बुरा है:

int& getInt() {
    int* i = new int;
    return *i;  // DON'T DO THIS.
}

क्योंकि अब ग्राहक को अंततः अजीब करना है:

int& myInt = getInt(); // note the &, we cannot lose this reference!
delete &myInt;         // must delete...totally weird and  evil

int oops = getInt(); 
delete &oops; // undefined behavior, we're wrongly deleting a copy, not the original

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

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

std::unique_ptr<int> getInt() {
    return std::make_unique<int>(0);
}

और अब ग्राहक एक स्मार्ट सूचक स्टोर करता है:

std::unique_ptr<int> x = getInt();

उन चीजों तक पहुंचने के लिए संदर्भ भी ठीक हैं जहां आप जानते हैं कि जीवनकाल उच्च स्तर पर खुला रखा जा रहा है, उदाहरण के लिए:

struct immutableint {
    immutableint(int i) : i_(i) {}

    const int& get() const { return i_; }
private:
    int i_;
};

यहां हम जानते हैं कि i_ संदर्भ को वापस करना ठीक है क्योंकि जो कुछ भी हमें बुला रहा है वह कक्षा के उदाहरण के जीवनकाल का प्रबंधन करता है, इसलिए i_ कम से कम लंबे समय तक जीवित रहेगा।

और निश्चित रूप से, इसमें कुछ भी गलत नहीं है:

int getInt() {
   return 0;
}

यदि जीवनकाल कॉलर तक छोड़ा जाना चाहिए, और आप केवल मूल्य की गणना कर रहे हैं।

सारांश: यदि ऑब्जेक्ट का जीवनकाल कॉल के बाद समाप्त नहीं होता है तो संदर्भ वापस करना ठीक है।


दो मामले हैं:

  • कॉन्स संदर्भ - अच्छा विचार, कभी-कभी, विशेष रूप से भारी वस्तुओं या प्रॉक्सी कक्षाओं, संकलक अनुकूलन के लिए

  • गैर-कॉन्स्ट संदर्भ - बाड विचार, कभी-कभी, encapsulations तोड़ता है

दोनों एक ही मुद्दे साझा करते हैं - संभावित रूप से नष्ट वस्तु को इंगित कर सकते हैं ...

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

इसके अलावा, निम्नलिखित नोट करें:

एक औपचारिक नियम है - सी ++ मानक (खंड 13.3.3.1.4 यदि आप रुचि रखते हैं) कहता है कि एक अस्थायी केवल एक कॉन्स्ट संदर्भ के लिए बाध्य हो सकता है - यदि आप एक गैर-कॉन्स्ट संदर्भ का उपयोग करने का प्रयास करते हैं तो संकलक को इसे ध्वजांकित करना होगा एक त्रुटि।


नहीं, नहीं, एक हजार बार नहीं।

बुराई क्या गतिशील आवंटित वस्तु का संदर्भ दे रही है और मूल सूचक को खो रही है। जब आप एक वस्तु को new करते हैं तो आप गारंटीकृत delete करने के लिए एक दायित्व मानते हैं।

लेकिन एक नजर डालें, उदाहरण के लिए, operator<< : उसे एक संदर्भ वापस करना होगा , या

cout << "foo" << "bar" << "bletch" << endl ;

काम नहीं करेगा


भयानक कोड के बारे में:

int& getTheValue()
{
   return *new int;
}

तो, वास्तव में, स्मृति सूचक वापसी के बाद खो दिया। लेकिन अगर आप उस तरह shared_ptr का उपयोग करते हैं:

int& getTheValue()
{
   std::shared_ptr<int> p(new int);
   return *p->get();
}

वापसी के बाद स्मृति खो नहीं गई है और असाइनमेंट के बाद मुक्त हो जाएगी।


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


मैं एक असली समस्या में भाग गया जहां यह वास्तव में बुरा था। अनिवार्य रूप से एक डेवलपर ने एक वेक्टर में किसी ऑब्जेक्ट का संदर्भ वापस कर दिया। वह खराब था!!!

जैनुररी में मैंने जो पूर्ण विवरण लिखा था: http://developer-resource.blogspot.com/2009/01/pros-and-cons-of-returing-references.html


रिटर्न संदर्भ आमतौर पर बड़े ऑब्जेक्ट के लिए सी ++ में ऑपरेटर अधिभार में उपयोग किया जाता है, क्योंकि एक मूल्य लौटने की प्रतिलिपि प्रतिलिपि की आवश्यकता होती है। (पेटर ओवरलोडिंग में, हम आम तौर पर सूचक मूल्य का उपयोग सूचक के रूप में नहीं करते हैं)

लेकिन वापसी संदर्भ स्मृति आवंटन समस्या का कारण बन सकता है। चूंकि परिणाम का संदर्भ फ़ंक्शन से रिटर्न मान के संदर्भ के रूप में पारित किया जाएगा, वापसी मान स्वचालित चर नहीं हो सकता है।

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

const max_tmp=5; 
Obj& get_tmp()
{
 static int buf=0;
 static Obj Buf[max_tmp];
  if(buf==max_tmp) buf=0;
  return Buf[buf++];
}
Obj& operator+(const Obj& o1, const Obj& o1)
{
 Obj& res=get_tmp();
 // +operation
  return res;
 }

इस तरह, आप सुरक्षित संदर्भ का उपयोग सुरक्षित रूप से कर सकते हैं।

लेकिन आप हमेशा functiong में मूल्य लौटने के संदर्भ के बजाय पॉइंटर का उपयोग कर सकते हैं।


सबसे अच्छी बात यह है कि ऑब्जेक्ट बनाना और इसे संदर्भ में पॉइंटर पैरामीटर के रूप में पास करना है जो इस चर को आवंटित करता है।

फंक्शन में ऑब्जेक्ट आवंटित करना और संदर्भ या सूचक के रूप में इसे वापस करना (पॉइंटर सुरक्षित है हालांकि) फ़ंक्शन ब्लॉक के अंत में स्मृति मुक्त करने के कारण बुरा विचार है।


    Class Set {
    int *ptr;
    int size;

    public: 
    Set(){
     size =0;
         }

     Set(int size) {
      this->size = size;
      ptr = new int [size];
     }

    int& getPtr(int i) {
     return ptr[i];  // bad practice 
     }
  };

getPtr फ़ंक्शन हटाने या यहां तक ​​कि एक शून्य ऑब्जेक्ट के बाद गतिशील मेमोरी तक पहुंच सकता है। जो खराब पहुंच अपवाद का कारण बन सकता है। इसके बजाय गेटटर और सेटर को लागू किया जाना चाहिए और लौटने से पहले आकार सत्यापित किया जाना चाहिए।





reference