C++ में एक वेक्टर को दूसरे वेक्टर में कैसे जोड़ें: push_back क्या छुपाता है

शुरुआती लोगों के लिए ऑनलाइन Python प्रशिक्षक

Python आसानी से सीखें बिना सिद्धांत के बोझ के। स्वचालित जांच के साथ व्यावहारिक कार्य हल करें और ब्राउज़र में सीधे कोड लिखें।

पाठ्यक्रम शुरू करें

परिचय: दस्तावेज़ क्या नहीं बताते

\n\n

कल्पना करें: आप एक उच्च-लोड सर्वर या डेटा प्रोसेसिंग एल्गोरिदम लिख रहे हैं। आपके पास दो std::vector<int> हैं, और आपको उन्हें एक में मर्ज करना है। लगता है, इससे आसान क्या हो सकता है? बस लो और जोड़ो। लेकिन C++ सतही दृष्टिकोण को माफ नहीं करता। एक गलत तरीका — और आपको अनावश्यक प्रतियां, प्रदर्शन में गिरावट, या यहां तक कि अपरिभाषित व्यवहार मिलता है।

\n\n

Stack Overflow पर «Appending a vector to a vector» प्रश्न ने हजारों बार देखा गया है, यह संयोग नहीं है। अनुभवी डेवलपर्स भी कभी-कभी push_back को insert समझ लेते हैं या move-सिमैंटिक्स भूल जाते हैं। इस लेख में हम कंपाइलर के अंदर झांकेंगे और समझेंगे कि एक वेक्टर को दूसरे में जोड़ना वास्तव में कैसे काम करता है। आप जानेंगे कि क्यों std::copy insert से तेज़ हो सकता है, और कब move-सिमैंटिक्स आपके प्रोजेक्ट को बचाएगा।

\n\n

सबसे स्पष्ट (और खतरनाक) तरीका: लूप में push_back

\n\n

शुरुआती अक्सर ऐसा लिखते हैं:

\n\n
std::vector<int> dest = {1, 2, 3};\nstd::vector<int> src = {4, 5, 6};\n\nfor (size_t i = 0; i < src.size(); ++i) {\n    dest.push_back(src[i]);\n}
\n\n

यहाँ क्या हो रहा है? प्रत्येक पुनरावृत्ति पर push_back जाँचता है कि पर्याप्त मेमोरी आवंटित है या नहीं। यदि नहीं, तो पुनःआवंटन होता है: पुरानी ऐरे को मेमोरी के नए क्षेत्र में कॉपी किया जाता है, पुरानी हटा दी जाती है। तीन तत्वों के लिए, 2-3 पुनःआवंटन हो सकते हैं। छोटे वेक्टर के लिए यह अदृश्य है, लेकिन हजारों तत्वों के लिए — यह आपदा है।

\n\n

समस्या: O(n) जाँच और पूरी ऐरे की संभावित प्रतिलिपियाँ। C++17 और नए में, कंपाइलर अनुकूलन कर सकता है, लेकिन इस पर भरोसा करना जोखिम भरा है।

\n\n

push_back के अंदर क्या होता है?

\n
    \n
  • size() < capacity() की जाँच।
  • \n
  • यदि नहीं, तो reallocate(new_capacity) को कॉल करें।
  • \n
  • मौजूदा तत्वों की प्रतिलिपि (या स्थानांतरण)।
  • \n
  • पुराने बफर को हटाना।
  • \n
  • नए तत्व की प्रतिलिपि।
  • \n
\n\n

निष्कर्ष: पूरे वेक्टर को जोड़ने के लिए कभी भी लूप का उपयोग न करें, जब तक कि आप जानबूझकर थोड़ी संख्या में तत्वों के लिए ऐसा नहीं कर रहे हों।

\n\n

सही तरीका: std::vector::insert

\n\n

मानक पुस्तकालय एक सुरुचिपूर्ण समाधान प्रदान करता है:

\n\n
std::vector<int> dest = {1, 2, 3};\nstd::vector<int> src = {4, 5, 6};\n\ndest.insert(dest.end(), src.begin(), src.end());
\n\n

एक पंक्ति! लेकिन इस कॉल के पीछे क्या छिपा है? insert निम्नलिखित करता है:

\n
    \n
  • आवश्यक मेमोरी की गणना: dest.size() + src.size()।
  • \n
  • यदि वर्तमान क्षमता अपर्याप्त है — तो आवश्यकतानुसार (या रिजर्व के साथ, कार्यान्वयन पर निर्भर करता है) आवंटित करता है।
  • \n
  • मौजूदा तत्वों को स्थानांतरित करता है (यदि अंत में नहीं डाल रहे हैं)।
  • \n
  • src से नए तत्वों की प्रतिलिपि (या स्थानांतरण) करता है।
  • \n
\n\n

महत्वपूर्ण: insert एक पुनःआवंटन करता है (या कोई नहीं, यदि जगह पर्याप्त है)। यह लूप से बेहतर है, लेकिन फिर भी src से तत्वों की प्रतिलिपि होती है।

\n\n

insert का प्रदर्शन

\n

यदि सम्मिलन के बाद src की आवश्यकता नहीं है, तो आप std::make_move_iterator का उपयोग करके इसके तत्वों को स्थानांतरित कर सकते हैं:

\n\n
dest.insert(dest.end(),\n            std::make_move_iterator(src.begin()),\n            std::make_move_iterator(src.end()));
\n\n

यह प्रतिलिपि को स्थानांतरण में बदल देता है। std::vector<int> के लिए कोई अंतर नहीं है (int एक तुच्छ प्रकार है), लेकिन std::vector<std::string> या जटिल वस्तुओं के लिए, स्थानांतरण प्रतिलिपि की तुलना में बहुत सस्ता है।

\n\n

विकल्प: back_inserter के साथ std::copy

\n\n

कुछ डेवलपर्स STL एल्गोरिदम पसंद करते हैं:

\n\n


                
                

ब्लॉग

पुस्तक अनुशंसाएँ