स्मार्ट शब्द "Move सेमैंटिक्स" के पीछे क्या छिपा है?
\n\nकल्पना करें कि आप एक नए अपार्टमेंट में शिफ्ट हो रहे हैं। आपके पास एक बहुत बड़ा, भारी सोफा है। क्या तेज़ होगा: इसे पाँच मंज़िलों की सीढ़ियों पर खुद ढोना या बस चाबियाँ मूवर्स को दे देना और कहना: "दोस्तों, सोफा आपका है, इसके साथ जो चाहे करें, मुझे इसकी अब ज़रूरत नहीं है"? दूसरा विकल्प ही C++ में move-सेमैंटिक्स है।
\n\nC++11 मानक के आने से पहले, प्रोग्रामर दर्दनाक रूप से डेटा कॉपी करते थे। हर बार जब आप किसी फ़ंक्शन से वेक्टर या स्ट्रिंग लौटाते थे, तो कंपाइलर एक सटीक प्रतिलिपि बनाता था। अगर वेक्टर में दस लाख तत्व होते थे - तो वह ईमानदारी से हर एक को कॉपी करता था। यह धीमा, अकुशल था और मूल को देने के बजाय किताब को हाथ से कॉपी करने जैसा था।
\n\nMove-सेमैंटिक्स (स्थानांतरण सेमैंटिक्स) एक ऐसा तंत्र है जो महँगी कॉपी किए बिना एक ऑब्जेक्ट से दूसरे ऑब्जेक्ट में संसाधनों (मेमोरी, फ़ाइल डिस्क्रिप्टर, सॉकेट) को स्थानांतरित करने की अनुमति देता है। स्थानांतरण के बाद, मूल ऑब्जेक्ट "खाली" लेकिन सही स्थिति में रहता है, और इसे सुरक्षित रूप से नष्ट किया जा सकता है या इसे एक नया मान दिया जा सकता है।
\n\nइस लेख में, हम rvalue-संदर्भों, std::move फ़ंक्शन, स्थानांतरण कंस्ट्रक्टर और असाइनमेंट ऑपरेटरों को सरल शब्दों में और कोड उदाहरणों के साथ समझेंगे।
\n\nMove-सेमैंटिक्स के बिना कोड धीमा क्यों चलता है?
\n\nआइए एक क्लासिक उदाहरण देखें जो 2011 से पहले सभी C++ प्रोग्रामरों को परेशान करता था:
\n\n#include <iostream>\n#include <vector>\n#include <string>\n\nstd::vector<std::string> createBigVector() {\n std::vector<std::string> vec(1000000, "Hello");\n return vec; // भयानक! दस लाख स्ट्रिंग्स की कॉपी!\n}\n\nint main() {\n std::vector<std::string> data = createBigVector();\n std::cout << "Size: " << data.size() << std::endl;\n return 0;\n}\n\nयहाँ क्या हो रहा है? फ़ंक्शन createBigVector एक वेक्टर बनाता है, उसे डेटा से भरता है और लौटाता है। कंपाइलर (बिना ऑप्टिमाइज़ेशन के) निम्नलिखित करता है:
- \n
- फ़ंक्शन के अंदर वेक्टर बनाता है। \n
- पूरे वेक्टर को लौटाए गए मान में कॉपी करता है। \n
- मूल वेक्टर को नष्ट करता है। \n
mainमें, इस मान को वेरिएबलdataमें कॉपी करता है। \n
परिणाम: दस लाख स्ट्रिंग्स की दो पूर्ण प्रतियाँ। यह सिर्फ धीमा नहीं है - यह प्रदर्शन के लिए एक आपदा है।
\n\nRvalue और Lvalue: C++ की दो दुनियाएँ
\n\nMove-सेमैंटिक्स को समझने के लिए, दो मूलभूत अवधारणाओं को समझना होगा: lvalue और rvalue।
\n\n- \n
- Lvalue (left value) — वह ऑब्जेक्ट जिसका मेमोरी में एक पता और एक नाम होता है। आप इसका पता ले सकते हैं (
&obj)। उदाहरण: वेरिएबलint x = 5;—xएक lvalue है। \n - Rvalue (right value) — एक अस्थायी मान जिसका कोई नाम नहीं होता और वह असाइनमेंट ऑपरेटर के बाईं ओर नहीं आ सकता। उदाहरण:
5,x + 3, फ़ंक्शन का परिणामgetTemp()। आप&(x + 3)नहीं लिख सकते। \n
पहले, rvalue-संदर्भ (जिसे && से दर्शाया जाता है) मौजूद नहीं था। कंपाइलर एक अस्थायी ऑब्जेक्ट देखता था और कहता था: "हाँ, यह एक rvalue है, मैं इसके साथ केवल एक ही काम कर सकता हूँ - इसे कॉपी करना या नष्ट करना।" Move-सेमैंटिक्स ने कंपाइलर को तीसरा विकल्प दिया: rvalue से संसाधनों को चुराना, क्योंकि यह वैसे भी जल्द ही नष्ट होने वाला है।
std::move और rvalue-संदर्भ कैसे काम करते हैं?
\n\nstd::move फ़ंक्शन केवल एक टाइप कास्ट है। यह वास्तव में डेटा को स्थानांतरित नहीं करता, बल्कि केवल lvalue को rvalue में बदल देता है, जिससे कंपाइलर को ऑब्जेक्ट को "लूटने" की अनुमति मिलती है।
\n\nएक सरल क्लास पर विचार करें जो डायनामिक मेमोरी का प्रबंधन करती है:
\n\n#include <iostream>\n#include <cstring>\n\nclass MyString {\nprivate:\n cha