2009-07-07 26 views
18

मैं सोच रहा था कि एक सूची लौटने की बजाय, एक सूचक को लौटने की बजाय, प्रदर्शन की अवधि में महंगा था क्योंकि अगर मुझे याद है, तो सूची में बहुत से गुण नहीं हैं (क्या यह 3 पॉइंटर्स की तरह नहीं है? एक वर्तमान स्थिति के लिए, शुरुआत के लिए एक और अंत के लिए एक?)।एक std :: सूची महंगा लौट रहा है?

उत्तर

33

यदि आप मूल्य के अनुसार std::list वापस लौटते हैं तो यह केवल सूची के शीर्ष की प्रतिलिपि नहीं करेगा, यह सूची में प्रति आइटम एक आइटम नोड कॉपी करेगा। तो हाँ, एक बड़ी सूची के लिए यह महंगा है।

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

मूल्य से कंटेनर लौटने से बचने के लिए सी ++ में एक सामान्य मुहावरे, आउटपुट इटरेटर को पैरामीटर के रूप में लेना है। तो बजाय:

std::list<int> getListOfInts() { 
    std::list<int> l; 
    for (int i = 0; i < 10; ++i) { 
     l.push_back(i); 
    } 
    return l; 
} 

आप कार्य करें:

template<typename OutputIterator> 
void getInts(OutputIterator out) { 
    for (int i = 0; i < 10; ++i) { 
     *(out++) = i; 
    } 
} 

तो फोन करने वाले से करता है:

std::list<int> l; 
getInts(std::back_inserter(l)); 

अक्सर एक बार संकलक इनलाइनिंग और अनुकूलन समाप्त हो गया है, कोड और अधिक या कम समान है ।

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

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

[संपादित करें: टी.ई.डी. एक टिप्पणी में बताता है कि टेम्पलेट का उपयोग किये बिना प्रतिलिपि से बचने का दूसरा तरीका कॉलर के संदर्भ में एक सूची में पास होना है। यह निश्चित रूप से काम करता है, यह सिर्फ कॉलर को टेम्पलेट की तुलना में कम लचीलापन देता है, और इसलिए एसटीएल द्वारा उपयोग किया जाने वाला मुहावरे नहीं है। यदि आप उपरोक्त वर्णित "इसका लाभ" नहीं चाहते हैं तो यह एक अच्छा विकल्प है। हालांकि, एसटीएल के पीछे मूल इरादे में से एक "एल्गोरिदम" को अलग करना है (इस मामले में जो भी मान निर्धारित करता है) "कंटेनर" से (इस मामले में, तथ्य यह है कि मूल्यों को सूची में संग्रहीत किया जाना चाहिए, जैसा कि विरोध किया गया है एक वेक्टर या एक सरणी या एक स्व-सॉर्टिंग सेट के लिए, या बस उन्हें संग्रहीत किए बिना मुद्रित)।]

+0

धन्यवाद, मैंने सोचा था कि यह केवल सिर की प्रतिलिपि बना रहा था! –

+3

दिलचस्प। संदर्भ में सूची क्यों न दें? –

+0

डर नहीं - किसी भी एसटीएल संग्रह की एक प्रति एक पूर्ण प्रति है (आइटम की प्रतियों सहित)। ऐसा इसलिए है कि जब आप प्रतिलिपि (या उसके तत्व) को संशोधित करते हैं तो आप मूल को भी संशोधित नहीं करते हैं। यहां तक ​​कि यदि यह पॉइंटर्स का संग्रह है, तो पॉइंटर्स को अभी भी कॉपी करना होगा, हालांकि निश्चित रूप से ऑब्जेक्ट्स की प्रतिलिपि बनाई गई है, और संग्रह के बीच अभी भी "साझा" नहीं हैं। –

0

मेरा मानना ​​है कि प्रति-निर्माता को बुलाया जाता है।

+0

वास्तव में यह है, लेकिन मैं सोच रहा था कि कॉपी-कन्स्ट्रक्टर क्या कर रहा था। धन्यवाद अभी भी! –

+0

मेरी सलाह: ब्रूस द्वारा सी ++ में सोचना खरीदें एकल, इस तरह के अधिकांश प्रश्न पहले पास के बाद चले जाएंगे ;-) – MadH

+0

एंड्रयू कोएनिग और बारबरा मू द्वारा त्वरित सी ++ खरीदें। –

0

यह महंगा हो सकता है, जिसमें यह सूची में प्रत्येक तत्व की प्रतिलिपि बनायेगा। सबसे महत्वपूर्ण बात यह है कि इसमें अलग-अलग व्यवहार हैं: क्या आप सूची की एक प्रति चाहते हैं या आप मूल सूची में सूचक चाहते हैं?

2

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

आपका मुख्य विकल्प प्रतिलिपि सुनिश्चित करने के लिए जगह नहीं ले जाएगा रहे हैं: संदर्भ द्वारा एक सूची में

  • दर्रे में समारोह द्वारा भरा जाना है। इस तरह आप उस समारोह को बताते हैं जहां डेटा डालना है और कोई प्रतिलिपि बनाने की आवश्यकता नहीं है क्योंकि आप इसे अपने अंतिम स्थान पर डालते हैं।
  • ढेर पर एक सूची आवंटित करें और इसे वापस करें। आपको इसे एक स्मार्ट पॉइंटर में std :: auto_ptr या boost :: shared_ptr जैसे इसे हटाने के लिए इसे हटा देना चाहिए और अपवाद सुरक्षित होना चाहिए।
5

यह (हमेशा के रूप में) निर्भर करता है। प्रतिलिपि कन्स्ट्रक्टर निम्नलिखित कोड में वापसी से लागू या नहीं किया जा सकता है।

std::list<int> foo() { 
    std::list<int> bar; 
    // ... 
    return bar; 
}; 

यह लागू नहीं किया जा सकता है, तो संकलक return value optimization लागू होता है। यदि कॉपी-कन्स्ट्रक्टर कहा जाता है, तो यह बड़ी सूचियों के लिए सूचक के लिए शायद अधिक महंगी है, और यदि इसे नहीं कहा जाता है, तो सीधे सूची को वापस करने के लिए तेज़ है (क्योंकि यह गतिशील आवंटन से बचाता है)

व्यक्तिगत रूप से, मुझे इसके बारे में चिंता नहीं है और सीधे सूची लौटाएं। फिर, केवल जब मेरा प्रोफाइलर कहता है कि यह एक समस्या है, तो मैं अनुकूलन पर विचार करता हूं।

+1

भी पढ़ा [यह] (http://cpp-next.com/archive/2009/08/want-speed-pass-by-value/) – andreabedini

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