2010-09-13 15 views
10

संभव डुप्लिकेट:
how to “return an object” in C++सी ++ वेक्टर, वापसी बनाम पैरामीटर

अगर वहाँ तीन निम्नलिखित दृष्टिकोणों के बीच एक अंतर है मैं सोच रहा हूँ:

void FillVector_1(vector<int>& v) { 
    v.push_back(1); // lots of push_backs! 
} 

vector<int> FillVector_2() { 
    vector<int> v; 
    v.push_back(1); // lots of push_backs! 
    return v; 
} 

vector<int> FillVector_3() { 
    int tab[SZ] = { 1, 2, 3, /*...*/ }; 
    return vector<int>(tab, tab + SZ); 
} 
+1

प्रदर्शन में? – Anycorn

+4

बस मूल्य से वापस आएं और प्रदर्शन के बारे में चिंता करें जब आपको लगता है कि यह वास्तव में एक समस्या है। फिर इसे प्रोफाइल करें ताकि आपको अनुमान लगाने की आवश्यकता न हो। मैं [डुप्लिकेट] के रूप में बंद कर रहा हूं (http://stackoverflow.com/questions/3350385/how-to-return-an-object-in-c)। – GManNickG

+1

मैंने इसे दो कारणों से डुप्लिकेट के रूप में बंद करने के लिए वोट नहीं दिया होगा: (1) पहले के प्रश्न में गतिशील स्मृति आवंटन के बारे में विशिष्ट जानकारी थी जो इस प्रश्न पर लागू नहीं होती है, और (2) यह प्रश्न एसटीएल-विशिष्ट है जो कर सकता है डालने वाले इटरेटर जैसे अन्य मुहावरे का आह्वान करें। साथ ही, एसटीएल के कुछ कार्यान्वयन पहले ही प्रतिलिपि को कम करने के लिए सी ++ 0x आर-मान संदर्भों का लाभ उठाते हैं। वह विधि पहले के प्रश्न में लागू नहीं होती है। –

उत्तर

16

सबसे बड़ा अंतर यह है कि पहला तरीका मौजूदा सामग्रियों में संलग्न होता है, जबकि अन्य दो खाली वेक्टर भरते हैं। :)

मुझे लगता है कि आप जिस कीवर्ड को खोज रहे हैं वह return value optimization है, जो सामान्य होना चाहिए (जी ++ के साथ आपको इसे लागू होने से रोकने के लिए इसे बंद करना होगा)। यही है, यदि उपयोग इस प्रकार है:

vector<int> vec = fill_vector(); 

तो वहां आसानी से कोई प्रतियां नहीं हो सकती हैं (और फ़ंक्शन का उपयोग करना आसान है)।

यदि आप किसी मौजूदा वेक्टर

vector<int> vec; 
while (something) 
{ 
    vec = fill_vector(); 
    //do things 
} 

के साथ काम कर रहे हैं तो एक बाहर पैरामीटर का उपयोग करने के चारों ओर एक पाश में वैक्टर और नकल डेटा के निर्माण से बच जाएंगे।

std::vector<int> v; 
FillContainer(std::back_inserter(v)); 

प्रदर्शन (और अन्य: यह के रूप में वेक्टर के साथ इस्तेमाल किया जा सकता

template<typename OutputIterator> 
void FillContainer(OutputIterator it) { 
    *it++ = 1; 
    ... 
} 

तब:

+1

+1 elision कॉपी करें। – sellibitze

2

पहले निश्चित रूप से वेक्टर की प्रतिलिपि नहीं करता है।

वेक्टर की प्रतिलिपि बनाने की लागत वेक्टर में तत्वों की संख्या में रैखिक हो सकती है।

पहले किसी प्लेटफ़ॉर्म या कंपाइलर पर रैखिक व्यवहार का कोई जोखिम नहीं है, और प्रोफाइलिंग और रीफैक्टरिंग की कोई लागत नहीं है।

+0

पहला भी एक वेक्टर को भरने के लिए पंक्ति में कई कार्यों को कॉल करने की अनुमति देता है। यदि आपको # 2 के साथ ऐसा करने की ज़रूरत है, तो आपको वापस आने वाले वेक्टर से तत्वों की प्रतिलिपि बनाना होगा (जिसका मतलब है कि संकलक द्वारा सभी वीर आरवीओ प्रयास कुछ भी नहीं होंगे, क्योंकि आप तुरंत- वैसे भी त्याग प्रतिलिपि)। –

6

कोई मानता है कि पैरामीटर सबसे अच्छा है, लेकिन व्यवहार में यह अक्सर नहीं होता है। यह कंपाइलर पर निर्भर करता है। कुछ कंपाइलर्स (मुझे लगता है कि वास्तव में सबसे हालिया कंपाइलर्स) Return Value Optimization - विजुअल स्टूडियो 2005 लागू करेंगे और बाद में आपके द्वारा प्रदान किए गए दोनों मामलों में इसे करना चाहिए (Named Return Value Optimization in Visual C++ 2005 देखें)।

निश्चित रूप से जानने के लिए सबसे अच्छा तरीका उत्पादित डिस्सेप्लर को देखना है।

+0

यह एक _huge_ अनुकूलन है, और अधिकांश आधुनिक कंपाइलर्स प्रदर्शन में बड़े लाभ के साथ इसका लाभ उठाते हैं। – fbrereto

+0

आरवीओ का पूरा विचार मुझे क्रिंग करता है। मुझे इससे घृणा है। प्रारंभिकरण और असाइनमेंट w.r.t. के बीच अंतर को इंगित करने के लिए –

0

एक नज़र में, पहले दो में शायद वेक्टर का अधिक आकार बदल रहा है, जबकि तीसरे व्यक्ति को वेक्टर के आकार को बदलने की आवश्यकता नहीं है। पुशबैक से पहले इसे स्वयं आकार देकर इसे कम किया जा सकता है।

5

मिश्रण में एक चौथे संस्करण जोड़ना:

void FillVector_4(vector<int>& v) { 
    static const int tab[SZ] = {1,2,3, ... }; 
    v.assign(tab,tab+SZ); 
} 

आप संकलक वापसी मान के लिए एक vector<int> प्रतिलिपि बनाने के कर सकते हैं प्रदर्शन संस्करण 2 और 3 संस्करण के बारे में सोच रहे हैं। यही है, अगर संकलक एनआरवीओ (नाम वापसी मूल्य अनुकूलन) नाम करने में सक्षम नहीं है। इसके अलावा, push_back एसके बिना लगातार वेक्टर की बढ़ने की आवश्यकता होने के बाद से कुछ पुनरावृत्ति की ओर जाता है। चाहे यह महत्वपूर्ण है, आपकी समस्या पर निर्भर करता है जिसे आप हल करने की कोशिश कर रहे हैं।

आपको यह जानकर प्रसन्नता होगी कि सी ++ 0x स्थानीय रूप से बनाए गए वेक्टर को बहुत ही कुशलतापूर्वक वापस कर देगा। मैं डेविड अब्राहम article series को गुजरने/वापस करने सहित कुशल मूल्य प्रकारों के बारे में पढ़ने की भी सिफारिश करता हूं।

9

मुहावरेदार सी ++ दृष्टिकोण एक निर्गम इटरेटर का उपयोग करके कंटेनर प्रकार से अधिक सार होगा गैर-खाली कंटेनर भरने में सक्षम होने के फायदे) आपके विकल्प # 1 के समान हैं। एक और अच्छी बात यह है कि इसका उपयोग स्ट्रीमिंग मोड में आउटपुट करने के लिए किया जा सकता है, जहां परिणाम तुरंत संसाधित किए जाते हैं और संग्रहीत किए बिना त्याग दिए जाते हैं, यदि उचित प्रकार के इटरेटर का उपयोग किया जाता है (उदा। ostream_iterator)।

+0

+1। यह सबसे अधिक "एसटीएल रास्ता" जैसा लगता है। ध्यान दें कि इटरेटर को 'फिलकॉन्टेनर' से वापस करने के लिए उपयोगी हो सकता है। –

+0

यह कैसे काम करता है जब "फिलकॉन्टेनर" कक्षा का एक तरीका है? – User

+0

@ उपयोगकर्ता: वैसे ही - वर्ग सदस्य कार्यों को भी टेम्पलेट किया जा सकता है। आप इस तरह की चीज से क्या नहीं कर सकते हैं इसे आभासी बनाते हैं। –

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