push_back बनाम emplace_back: क्या अंतर है और शुरुआती को क्या चुनना चाहिए?

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

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

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

परिचय: यह क्यों महत्वपूर्ण है?

\n\n

क्या आपने कभी सोचा है कि C++ में वेक्टर के अंत में एक तत्व जोड़ने के दो तरीके क्यों हैं? ऐसा लगता है, अगर पहले से सिद्ध push_back मौजूद है तो पहिया का पुनः आविष्कार क्यों करें? इसका उत्तर आधुनिक C++ के प्रदर्शन और लचीलेपन में छिपा है।

\n\n

Stack Overflow पर इस प्रश्न ने हजारों बार देखा गया और सैकड़ों टिप्पणियाँ प्राप्त की हैं। और यह कोई संयोग नहीं है: push_back और emplace_back के बीच अंतर को समझना केवल एक शैक्षणिक ज्ञान नहीं है, बल्कि एक व्यावहारिक कौशल है जो आपके कोड को 2-3 गुना तेज कर सकता है। आइए इसे एक बार और हमेशा के लिए समझ लें।

\n\n

मूलभूत अंतर: दस्तावेज़ क्या कहते हैं?

\n\n

आइए सरल से शुरू करें। दोनों विधियाँ कंटेनर (वेक्टर, सूची, कतार) के अंत में एक तत्व जोड़ती हैं। लेकिन वे इसे अलग-अलग तरीके से करती हैं:

\n\n
    \n
  • push_back — एक तैयार ऑब्जेक्ट (मान, संदर्भ या rvalue-संदर्भ द्वारा) लेता है और इसे कंटेनर में कॉपी/मूव करता है।
  • \n
  • emplace_back — ऑब्जेक्ट के कंस्ट्रक्टर के लिए आर्गुमेंट लेता है और इसे सीधे कंटेनर के अंदर बनाता है (in-place construction)।
  • \n
\n\n

अमूर्त लगता है? आइए कोड देखें।

\n\n

व्यावहारिक उदाहरण: एक क्लास बनाना

\n\n

मान लीजिए हमारे पास एक सरल क्लास Person है:

\n\n
#include <iostream>\n#include <vector>\n#include <string>\n\nclass Person {\npublic:\n    std::string name;\n    int age;\n\n    // कंस्ट्रक्टर\n    Person(const std::string& n, int a) \n        : name(n), age(a) {\n        std::cout << "कंस्ट्रक्टर " << name << " के लिए कॉल किया गया" << std::endl;\n    }\n\n    // कॉपी कंस्ट्रक्टर\n    Person(const Person& other) \n        : name(other.name), age(other.age) {\n        std::cout << "कॉपी कंस्ट्रक्टर " << name << " के लिए" << std::endl;\n    }\n\n    // मूव कंस्ट्रक्टर\n    Person(Person&& other) noexcept\n        : name(std::move(other.name)), age(other.age) {\n        std::cout << "मूव कंस्ट्रक्टर " << name << " के लिए" << std::endl;\n    }\n};
\n\n

अब देखते हैं कि विभिन्न विधियों का उपयोग करने पर क्या होता है।

\n\n

विकल्प 1: अस्थायी ऑब्जेक्ट के साथ push_back

\n\n
int main() {\n    std::vector<Person> people;\n    people.push_back(Person("Alice", 25));\n    return 0;\n}
\n\n

आउटपुट:

\n
कंस्ट्रक्टर Alice के लिए कॉल किया गया\nमूव कंस्ट्रक्टर Alice के लिए
\n\n

क्या हुआ? पहले एक अस्थायी Person ऑब्जेक्ट बनाया गया (कंस्ट्रक्टर का पहला कॉल)। फिर यह ऑब्जेक्ट वेक्टर में मूव हो गया (मूव कंस्ट्रक्टर)। दो कॉल — दोगुना काम।

\n\n

विकल्प 2: तैयार ऑब्जेक्ट के साथ push_back

\n\n
int main() {\n    std::vector<Person> people;\n    Person alice("Alice", 25);\n    people.push_back(alice);  // alice — lvalue\n    return 0;\n}
\n\n

आउटपुट:

\n
कंस्ट्रक्टर Alice के लिए कॉल किया गया\nकॉपी कंस्ट्रक्टर Alice के लिए
\n\n

यहाँ हम मूव के बजाय कॉपी देखते हैं। यदि ऑब्जेक्ट बड़ा है (जैसे, डायनामिक ऐरे शामिल हैं), तो कॉपी करना महंगा हो सकता है।

\n\n

विकल्प 3: emplace_back

\n\n
int main() {\n    std::vector<Person> people;\n    people.emplace_back("Bob", 30);  // कंस्ट्रक्टर के लिए आर्गुमेंट पास करें\n    return 0;\n}
\n\n

आउटपुट:

\n
कंस्ट्रक्टर Bob के लिए कॉल किया गया
\n\n

कंस्ट्रक्टर का केवल एक कॉल! Person ऑब्जेक्ट सीधे वेक्टर की मेमोरी में बनाया जाता है, बिना किसी मध्यवर्ती कॉपी या मूव के।

\n\n

प्रदर्शन: अंतर कब महत्वपूर्ण है?

\n\n

सरल प्रकारों (int, double, पॉइंटर्स) के लिए अंतर नगण्य है — कंपाइलर सब कुछ ऑप्टिमाइज़ कर देता है। लेकिन जटिल ऑब्जेक्ट्स के लिए अंतर महत्वपूर्ण हो सकता है:

\n\n
    \n
  • भारी कॉपी कंस्ट्रक्टर वाले ऑब्जेक्ट्स (जैसे, std::string, कंटेनर

ब्लॉग

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