2012-02-26 13 views
29

जब इस कदम निर्माणकर्ता और चाल काम ऑपरेटरों को लागू करने, एक अक्सर इस तरह कोड लिखते हैं:एक पॉइंटर वैरिएबल को क्यों स्थानांतरित करना इसे शून्य पर सेट नहीं करता है?

p = other.p; 
other.p = 0; 

परोक्ष परिभाषित कदम संचालन इस तरह कोड के साथ लागू किया जाएगा:

p = std::move(other.p); 

कौन सा गलत होगा, क्योंकि एक पॉइंटर वैरिएबल को पर ले जाने से इसे शून्य पर सेट किया जाता है। ऐसा क्यों है? क्या कोई मामला है कि हम कदम सूचकांक को मूल सूचक परिवर्तक को अपरिवर्तित छोड़ना चाहते हैं?

नोट: "चलती", मैं नहीं सिर्फ उपसूचक std::move(other.p) मतलब है, मैं पूरी अभिव्यक्ति p = std::move(other.p) मतलब है। तो, ऐसा कोई विशेष भाषा नियम क्यों नहीं है जो कहता है "यदि असाइनमेंट का दायां हाथ एक सूचक xvalue है, तो यह असाइनमेंट के बाद शून्य हो गया है।"

+5

क्यों होना चाहिए? एकमात्र चीज जिसे आप 'स्थानांतरित' ऑब्जेक्ट के साथ करना चाहते हैं उसे अनदेखा कर दिया जाता है। कक्षा के स्वामित्व वाली स्मृति को पॉइंटर पॉइंट होने पर कोई समस्या नहीं होनी चाहिए यदि आप अब पॉइंटर का उपयोग नहीं करते हैं, है ना? – hvd

+5

"आप जो भी उपयोग नहीं करते हैं उसके लिए आप भुगतान नहीं करते हैं"? –

+7

@hvd: अगर यह कहता है कि 'हटाएं पी' :) – fredoverflow

उत्तर

27

जाने के बाद अशक्त करने के लिए एक कच्चे सूचक की स्थापना इसका अर्थ है कि सूचक:

struct foo 
{ 
    bar* shared_factory; // This can be the same for several 'foo's 
         // and must never null. 
}; 

संपादित

यहाँ एक उद्धरण मानक से लगभग MoveConstructibe है स्वामित्व का प्रतिनिधित्व करता है। हालांकि, संबंधों का प्रतिनिधित्व करने के लिए बहुत से पॉइंटर्स का उपयोग किया जाता है। इसके अलावा, लंबे समय तक यह सिफारिश की जाती है कि कच्चे सूचक का उपयोग करने से स्वामित्व संबंधों का अलग-अलग प्रतिनिधित्व किया जाता है। उदाहरण के लिए, आप जिस स्वामित्व संबंध का जिक्र कर रहे हैं उसका प्रतिनिधित्व std::unique_ptr<T> द्वारा किया जाता है। यदि आप चाहते हैं कि निहित रूप से जेनरेट किए गए कदम संचालन आपके स्वामित्व का ख्याल रखें, आपको केवल उन सदस्यों का उपयोग करना है जो वांछित स्वामित्व व्यवहार का प्रतिनिधित्व करते हैं (और कार्यान्वित करते हैं)।

इसके अलावा, जनरेटेड चाल संचालन का व्यवहार प्रतिलिपि संचालन के साथ किया गया था: वे स्वामित्व धारणाएं भी नहीं करते हैं और ऐसा नहीं करते हैं। यदि एक सूचक की प्रतिलिपि बनाई जाती है तो एक गहरी प्रति। यदि आप ऐसा करना चाहते हैं तो आपको प्रासंगिक अर्थशास्त्र के उपयुक्त वर्ग एन्कोडिंग को भी बनाने की आवश्यकता है।

+3

मुझे बस एक पेपर पढ़ने को याद आया जिसमें कहा गया था कि "चलना कॉपी करने से अधिक महंगा नहीं होना चाहिए" या ऐसा कुछ। मूल पॉइंटर नल सेट करना उस नियम को तोड़ देगा। क्या आप जानते हैं कि मैं किस पेपर के बारे में बात कर रहा हूं, या मेरे दिमाग ने इसे बनाया है? – fredoverflow

+1

+1। अच्छे खर्च। मुझसे बेहतर! – Nawaz

4

मुझे लगता है कि जवाब है: इस तरह के व्यवहार को लागू करना स्वयं बहुत छोटा है और इसलिए मानक को संकलक पर किसी भी नियम को लागू करने की आवश्यकता महसूस नहीं होती है। सी ++ भाषा बहुत बड़ी है और इसके उपयोग से पहले सबकुछ कल्पना नहीं की जा सकती है। उदाहरण के लिए, सी ++ टेम्पलेट ले लो। इसे पहली बार इस्तेमाल नहीं किया जाने वाला तरीका इस्तेमाल नहीं किया गया था (यानी यह मेटाप्रोग्रामिंग क्षमता है)। तो मुझे लगता है कि मानक केवल स्वतंत्रता देता है, और std::move(other.p) के लिए कोई विशिष्ट नियम नहीं बनाता है, इसमें से एक के बाद डिजाइन-सिद्धांत: "आप जो भी उपयोग नहीं करते हैं उसके लिए आप भुगतान नहीं करते हैं"

हालांकि, std::unique_ptr चल रहा है, हालांकि कॉपी करने योग्य नहीं है। तो अगर आप सूचक-अर्थ जो चल रहा है और दोनों प्रतिलिपि बनाने योग्य चाहते हैं, तो यहाँ एक तुच्छ दिया गया है:

template<typename T> 
struct movable_ptr 
{ 
    T *pointer; 
    movable_ptr(T *ptr=0) : pointer(ptr) {} 
    movable_ptr<T>& operator=(T *ptr) { pointer = ptr; return *this; } 
    movable_ptr(movable_ptr<T> && other) 
    { 
     pointer = other.pointer; 
     other.pointer = 0; 
    } 
    movable_ptr<T>& operator=(movable_ptr<T> && other) 
    { 
     pointer = other.pointer; 
     other.pointer = 0; 
     return *this; 
    } 
    T* operator->() const { return pointer; } 
    T& operator*() const { return *pointer; } 

    movable_ptr(movable_ptr<T> const & other) = default; 
    movable_ptr<T> & operator=(movable_ptr<T> const & other) = default; 
}; 

अब आप कक्षाओं में लिख सकते हैं, अपने खुद के चाल-अर्थ विज्ञान लेखन के बिना:

struct T 
{ 
    movable_ptr<A> aptr; 
    movable_ptr<B> bptr; 
    //... 

    //and now you could simply say 
    T(T&&) = default; 
    T& operator=(T&&) = default; 
}; 

नोट कि आपको अभी भी प्रति-अर्थशास्त्र और विनाशक लिखना है, movable_ptr स्मार्ट पॉइंटर नहीं है।

+0

लेकिन निहित रूप से परिभाषित चाल ऑपरेशन 'move_Ptr' को कॉल नहीं करेंगे ... – fredoverflow

+3

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

+0

@ बोपरसन: मुझे लगता है कि यह एक जवाब होना चाहिए? – Nawaz

0

उदाहरण के लिए, यदि आपके पास किसी साझा ऑब्जेक्ट पर पॉइंटर है। याद रखें, किसी ऑब्जेक्ट को स्थानांतरित करने के बाद आंतरिक रूप से सुसंगत स्थिति में रहना चाहिए, इसलिए एक पॉइंटर सेट करना जो शून्य मान पर शून्य नहीं होना चाहिए, सही नहीं है।

Ie:

T u = rv; 
... 
rv’s state is unspecified [ Note:rv must still meet the requirements 
of the library component that is using it. The operations listed in 
those requirements must work as specified whether rv has been moved 
from or not. 
+0

लेकिन पॉइंटर वैरिएबल को नल पर सेट करना केवल xvalues ​​पर होगा। ग्राहक बाद में शून्य सूचक का निरीक्षण करने में सक्षम नहीं होगा। – fredoverflow

+0

क्या होगा यदि आपका विनाशक, उदाहरण के लिए, सूचक को किसी भी तरह का उपयोग करता है और यदि यह शून्य है तो यूबी का आह्वान करेगा? इसके अलावा, मैं मानक से उद्धरण के साथ अपने दावे का बैक अप नहीं ले सकता, लेकिन जहां तक ​​मुझे याद है, वस्तु को स्थानांतरित करने के बाद वैध होना चाहिए (इसमें आप इसे सामान्य रूप से उपयोग जारी रख सकते हैं), न केवल विनाशकारी। – doublep

+0

@FredOverflow: मानक में प्रासंगिक उद्धरण मिला। – doublep

5

मूविंग स्थानांतरित वस्तु से "अमान्य" प्रस्तुत करता है। यह स्वचालित रूप से इसे एक सुरक्षित "खाली" स्थिति में सेट करता है। सी ++ के लंबे समय से चलने वाले सिद्धांत के अनुसार "जो भी आप उपयोग नहीं करते हैं उसके लिए भुगतान न करें", यह आपकी नौकरी है यदि आप इसे चाहते हैं।

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