2009-10-07 16 views
55

मैं इस बारे में सोच रहा हूं क्योंकि स्कोप मुद्दों के कारण। उदाहरण के लिए, कोडक्या रिटर्न स्टेटमेंट कॉपी मान

typedef struct { 
    int x1;/*top*/ 
    int x2;/*bottom*/ 
    int id; 
} subline_t; 



subline_t subline(int x1, int x2, int id) { 
    subline_t t = { x1, x2, id }; 
    return t; 
} 

int main(){ 
    subline_t line = subline(0,0,0); //is line garbage or isn't it? the reference 
    //to subline_t t goes out of scope, so the only way this wouldn't be garbage 
    //is if return copies 
} 

तो मेरा प्रश्न है, क्या रिटर्न स्टेटमेंट हमेशा कॉपी करेगा? इस मामले में ऐसा लगता है, इसलिए मुझे विश्वास है कि रिटर्न प्रतिलिपि बनाता है। अगर यह प्रतिलिपि करता है, तो क्या यह हर मामले में कॉपी करेगा?

+0

आपके कोड में 'subline_t' के लिए कोई संदर्भ नहीं है (जैसा कि' & '- एक सी ++ संदर्भ) है। –

+0

@gmatt: क्या आप जावा/सी # की तरह सोच रहे हैं, जहां हर वस्तु कुछ का संदर्भ है? यह सी/सी ++ दुनिया में मामला नहीं है। – Donotalo

+0

@ डोनोटलो: आपको सच कहने के लिए, स्थानीय इलाके में एक चर लौटने पर मैं बस सी/सी ++ के व्यवहार को नहीं जानता था। खैर, मुझे पता था कि उदाहरण के लिए पूर्णांक के साथ यह ठीक है, लेकिन मुझे यह भी पता था कि आपको कुछ मामलों में सावधान रहना होगा (जैसे किसी ऑब्जेक्ट को स्थानीय रूप से तत्काल ऑब्जेक्ट पर पॉइंटर लौटना।) मुझे यह नहीं पता था कि सी/सी ++ structs के लिए गोद ले। – ldog

उत्तर

33

हां, उस मामले में एक प्रतिलिपि बनाई जाएगी। यदि आप इस तरह की फ़ंक्शन घोषणा बदलते हैं:

subline_t &subline(int x1, int x2, int id) { 

तब कोई प्रतिलिपि नहीं बनाई जाएगी। हालांकि, आपके विशिष्ट मामले में यह स्टैक पर आवंटित ऑब्जेक्ट का संदर्भ वापस करने के लिए मान्य नहीं होगा। समस्या यह है कि कॉलर को इसका उपयोग करने का मौका मिलने से पहले ऑब्जेक्ट को नष्ट कर दिया जाएगा और अमान्य कर दिया जाएगा।

यह C++ के लिए सामान्य Return Value Optimization से संबंधित है जो आपके द्वारा वर्णित मामले में वास्तविक प्रतिलिपि संचालन करने से बच सकता है। अंतिम परिणाम (या होना चाहिए) जैसा कि एक प्रति किया गया था, लेकिन आपको अनुकूलन के बारे में पता होना चाहिए। इस अनुकूलन की उपस्थिति, कुछ मामलों में, कार्यक्रम के देखने योग्य व्यवहार को बदल सकती है।

+0

का उपयोग नहीं करता है तो आपके द्वारा प्रस्तुत लिंक कहता है कि मानक ऑप्टिमाइज़ेशन की अनुमति देता है, जब तक कि यह प्रोग्राम के व्यवहार को नहीं बदलता: "सामान्य रूप से, सी ++ मानक एक संकलक को किसी भी अनुकूलन करने की अनुमति देता है, जब तक परिणामस्वरूप निष्पादन योग्य एक ही अवलोकन योग्य व्यवहार प्रदर्शित करता है जैसे कि मानक की सभी आवश्यकताओं को पूरा किया गया है " – Tom

+8

यदि आपने आगे पढ़ा है, तो आप यह भी देखेंगे कि लिंक आलेख बाद में कहता है: "रिटर्न वैल्यू ऑप्टिमाइज़ेशन शब्द सी ++ मानक में एक विशेष खंड को संदर्भित करता है जो एक रिटर्न स्टेटमेंट के परिणामस्वरूप एक प्रतिलिपि ऑपरेशन को छोड़ने के लिए कार्यान्वयन की अनुमति देता है, भले ही कॉपी कन्स्ट्रक्टर के साइड इफेक्ट्स हों, कुछ ऐसा जो कि अनुमति नहीं है जैसा कि खुद में शासन है। " –

+1

@ ग्रेग - पहली नजर में, आप स्थानीय चर के संदर्भ को वापस करने का सुझाव दे रहे हैं - यानी एक लटकते सूचक। पीओडी के साथ, यह विशेष कार्यक्रम अभ्यास में काम करेगा, लेकिन ... – Steve314

4

आपके मामले में, यह एक प्रति

यदि आपका कोड

subline_t& subline(int, int) 

था तो यह एक संदर्भ है, जो अपरिभाषित व्यवहार में प्राप्त होते हैं वापसी होगी वापस आ जाएगी।

+3

संदर्भ लौटने से स्वयं में और अनिश्चित व्यवहार नहीं होता है। स्थानीय के लिए संदर्भ लौटाना, लेकिन वैश्विक, स्थैतिक, 'इस' के क्षेत्र, या ढेर-आवंटित वस्तु का संदर्भ वापस करना बिल्कुल ठीक है। –

+0

हालांकि ढेर-आवंटित वस्तुओं के साथ सावधान रहें - यदि आप संदर्भ वापस करते हैं तो कॉलर शायद उस ऑब्जेक्ट को हटाने के लिए ज़िम्मेदार होने की अपेक्षा नहीं करेगा। – Steve314

+0

@ स्टीव 314: पावेल ने यह नहीं कहा कि कॉलर इसे मुक्त करने के लिए ज़िम्मेदार था, उन्होंने कहा कि यह ढेर आवंटित किया गया था। हो सकता है कि कोई और इसे मुक्त करने के लिए ज़िम्मेदार है, लेकिन "सबलाइन" कॉल इसका एक दृश्य देता है। ऐसा तब हो सकता है जब उदाहरण के लिए "उपलाइन" कैश किए गए मान लौटा रहा हो। स्पष्ट रूप से कॉलर को ऑब्जेक्ट के गतिशील दायरे को जानने की आवश्यकता होगी (यानी, यह मान्य रहने के लिए कितनी देर तक निर्भर किया जा सकता है)। –

0

संरचना के लिए subline_t आपने परिभाषित किया है, हाँ, यह हमेशा एक प्रतिलिपि वापस करेगा।

2

हाँ, एक समारोह के लिए मामलों में एक struct, की एक ऐसी struct इसे कॉपी जाएगा (लेकिन संकलक प्रतिलिपि दूर अनुकूलन करने के लिए शक्ति दी गई है return वापस जाने के लिए, अनिवार्य रूप से जहां यह साबित कर सकते हैं अनुकूलन शब्दार्थ अहानिकर है की घोषणा की, आप कर सकते हैं कारण "जैसा कि" प्रतिलिपि की गारंटी थी)।

हालांकि, ने इसे सी ++ के रूप में टैग किया है, सी नहीं, क्यों नहीं, struct को कन्स्ट्रक्टर के साथ आपूर्ति क्यों नहीं करें ...? लगता है स्पष्ट और अधिक प्रत्यक्ष ... -!)

+1

एक कॉपी कन्स्ट्रक्टर के साथ 'स्ट्रक्चर' की आपूर्ति क्यों करें जब यह डिफ़ॉल्ट कॉपी कन्स्ट्रक्टर से कुछ अलग नहीं करता है? इसके अलावा, ऐसा करने से यह गैर-पीओडी बन जाएगा, जो कहीं और महत्वपूर्ण हो सकता है। –

+0

यदि एक संरचना के बजाय मैंने सी ++ में एक कक्षा का उपयोग किया, तो क्या यह अभी भी कॉपी करेगा? – ldog

+0

मेरा मतलब है, अगर कोड कक्षा में संरचना के केवल सिंथेटिक परिवर्तन के समान था। – ldog

2

हाँ, वापसी एक प्रति

subline_t subline(int x1, int x2, int id) { 
     subline_t t = { x1, x2, id }; 
     return t; 
} 

आप एक referencer डाल है, तो इसका एक प्रति नहीं है

subline_t & subline(int x1, int x2, int id) { 
     subline_t t = { x1, x2, id }; 
     return t; // will result in corruption because returning a reference 
} 
+3

तकनीकी रूप से सही होने पर, मुझे लगता है कि कम से कम आपको सी ++ नए शौक के लिए "लेकिन आपको ऐसा नहीं करना चाहिए" जोड़ना चाहिए। –

5

यह हमेशा होगा एक प्रतिलिपि वापस करें।

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

+2

रिटर्न वैल्यू ऑप्टिमाइज़ेशन पर भरोसा करना अक्सर बुद्धिमान हो सकता है। मुझे लगता है कि जीसीसी यह भी करता है कि -00 के साथ भी और यदि आप प्रतिलिपि बनाना चाहते हैं तो आपको -फनो-एलिइड-कन्स्ट्रक्टर स्विच का उपयोग करने की आवश्यकता होगी। – UncleBens

-2

बस एफवाईआई, क्योंकि इस मामले में आप केवल एक संरचना (ure) का उपयोग कर रहे हैं, यह सी भाषा में समान व्यवहार है।

हालांकि इस एक भाषा की सुविधा है, यह है सिफारिश की है कि यह सी में

+0

प्रश्न में भाषा सुविधा क्या है, और यह क्यों अनुशंसा की जाती है कि इसका उपयोग नहीं किया जाना चाहिए? –

+0

@ पावेल, यह एक अच्छा सवाल है। ऐसा इसलिए है क्योंकि, हम डेटा को दो बार कॉपी करेंगे। एक बार नियमित रूप से संरचना को स्थापित करने के दौरान, और एक बार वापसी डेटा की प्रतिलिपि बनाते समय। – Alphaneo

+0

@Alphaneo, कृपया आरवीओ पर पढ़ें ... –

0

returing वस्तुओं नहीं किया जाएगा ++ मूल्य से और नहीं किया संदर्भ द्वारा।

संदर्भ subline_t को टी गुंजाइश

नहीं से बाहर चला जाता है, वस्तु copyed है।

वापसी कथन हमेशा

हाँ कॉपी करेंगे और नहीं ... शब्दार्थ यह नकल की तरह बर्ताव करता है, लेकिन वहाँ कुछ है कि वापसी मान अनुकूलन कि प्रतिलिपि निर्माता बचाता है कहा जाता है।

foo make_foo() 
{ 
    foo f(1,2,3); 
    return f; 
} 

foo ff=make_foo(); /// ff created as if it was created with ff(1,2,3) -- RVO 
foo ff2; 
ff2=make_foo(); /// instance of foo created and then copied to ff2 and then old 
       /// instance destroyed 
+0

की तरह एक छोटी सी संरचना की प्रतिलिपि बनाने से धीमा हो सकता है, लेकिन इस बात की कोई गारंटी नहीं है कि आरवीओ किक करेगा। कंपाइलर के पास अभी भी प्रतिलिपि बनाने की स्वतंत्रता है, और यह _must_ जांचें एक वैध प्रतिलिपि कन्स्ट्रक्टर की उपस्थिति, भले ही वह प्रतिलिपि को चुनने का विकल्प चुनती हो। –

1

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

हालांकि, सी ++ में ऐसा करने का मूर्ख तरीका रचनाकारों और असाइनमेंट सूचियों के साथ है। यह कोड और डेटा संरचनाओं को बेहतर ढंग से समाहित करता है, और आपको इंटरमीडिएट ऑब्जेक्ट्स की पर्याप्तता से बचने की अनुमति देता है जो कंपाइलर्स निर्माण/विनाश/प्रतिलिपि के लिए स्वतंत्र हैं।

struct subline_t { 
     int x1;/*top*/ 
     int x2;/*bottom*/ 
     int id; 

// constructor which initialises values with assignment list. 
    subline_t(int the_x1, int the_x2, int the_id) : 
    x1(the_x1), 
    x2(the_x2), 
    id(the_id) 
    { 
    } 
}; 


int main(){ 
    subline_t line2(0,0,0); // never requires a copy or assignment. 
} 
0

लौटा हुआ वर्ग या संरचना प्रतिलिपि बनाई जा सकती है या नहीं, भले ही संकलक प्रतिलिपि का उपयोग करता हो। What are copy elision and return value optimization? के उत्तरों को देखें, संक्षेप में, चाहे यह कॉपी किया गया हो या नहीं, कई चीजों पर निर्भर करता है।

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

अंत में, यदि आप कॉपी एलिशन पर भरोसा नहीं कर सकते हैं और आप प्रतियों से बचना चाहते हैं, तो आप संदर्भ के बजाय unique_ptr का उपयोग और वापसी कर सकते हैं। ऑब्जेक्ट की प्रतिलिपि नहीं बनाई जाएगी, हालांकि unique_ptr स्वयं कॉपी हो सकता है या नहीं (फिर से, कॉपी एलिशन पर निर्भर करता है!)। unique_ptr की प्रतिलिपि बनाना/स्थानांतरित करना बहुत सस्ता है यदि unique_ptr की प्रतिलिपि कुछ कारणों से नहीं होती है।

#include <memory> 

struct A { 
public: 
    int x; 
    int y; 

    A(int x, int y) : x(x), y(y) { 
    } 
}; 

std::unique_ptr<A> returnsA() { 
    return std::make_unique<A>(3, 4); 
} 

int main() { 
    auto a = returnsA(); 
} 

ध्यान दें कि आप (दुर्भाग्य से) एक निर्माता अपने struct के लिए घोषित करना चाहिए, वरना make_unique सी के ++ अपर्याप्तता के कारण संकलन नहीं होगा:

यहाँ एक उदाहरण unique_ptr का उपयोग कर रहा है।

संबंधित मुद्दे