2012-04-01 7 views
10

सप्ताहांत को कम करना मैं अपने सी ++ कौशल को रीफ्रेश करने और कुछ सी ++ 11 सीखने की कोशिश कर रहा हूं, मैंने निम्न समस्या पर ठोकर खाई है: मैं अपने कंटेनर वर्ग को ठीक से मजबूर नहीं कर सकता उपयोग चाल निर्माता: इस प्रकारसंरक्षक को std :: vector पास करना और semantics को स्थानांतरित करना

class builder 
{ 
    ... 
    container build() const 
    { 
    std::vector<items> items; 

    //... fill up the vector 

    return container(items); //should move the vector right? wrong! 
    //return container(std::move(items)); also doesn't work 
    } 
} 

और कक्षाओं आइटम और कंटेनर में परिभाषित किया गया,:

class container 
{ 
public: 

    container(std:vector<item> items) 
     : items_(items) // always invokes copy constructor on vector, never move 
    { } 

    container(container&& rhs) 
    { 
     ... 
    } 

    ... 

private: 
    std::vector<item> items_; 

} 

class item 
{ 
public: 
    //move .ctor 
    item(item && rhs); 
    item& operator=(item && rhs); 

    //copy .ctor 
    item(const item& rhs); //this gets called instead of move .ctor 
    item& operator=(const item& rhs); 

    ... 
} 

अब मेरी कोड रों

मैं एक बिल्डर वर्ग है, जो परिभाषित किया गया है इस प्रकार है

builder my_builder; 
... 
auto result = my_builder.build(); 

जो हर आइटम पहले निर्माण किया जा करने के लिए और उसके बाद की प्रतिलिपि बनाई का कारण बनता है का उपयोग करता है ... मतलब

मैं क्लासेस आइटम की प्रतिलिपि नहीं कैसे निम्नलिखित लिखना चाहिए? क्या मुझे मानक पॉइंटर्स का उपयोग करने के लिए वापस जाना चाहिए?

+0

'item2' क्या है? – Mankarse

+0

@Mankarse यह एक टाइपो था, – ghord

उत्तर

21

आपका कोड इस के लिए बदल दिया जाना चाहिए:

container(std:vector<item2> items) // may copy OR move 
: items_(std::move(items)) // always moves 
{} 

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

इसके अलावा: return container(std::move(items));। मैंने पहले इसका उल्लेख नहीं किया क्योंकि मैंने गलती से सोचा था कि सभी स्थानीय चर स्वचालित रूप से रिटर्न स्टेटमेंट में स्थानांतरित हो गए थे, लेकिन केवल लौटाया गया मूल्य है। (तो यह वास्तव में काम करना चाहिए: return items;, क्योंकि container के निर्माता explicit नहीं है।)

+1

को संशोधित किया गया था, इस सामान्य नियम का अपवाद यह है कि यदि आप बहुत बड़ी वस्तुओं को पारित कर रहे हैं जो स्थानांतरित होने के लिए महंगे होते हैं, या जब आप होते हैं तो आपको रावल्यू संदर्भ (मूल्य के बजाए) पास करना चाहिए। जेनेरिक कोड लिखना जो पारित वस्तुओं के प्रकारों के बावजूद कुशल होने के लिए है। – Mankarse

+0

इस मामले के बारे में क्या होगा जब कोई इस कन्स्ट्रक्टर को वेक्टर के साथ आमंत्रित करता है जिसे बाद में उपयोग किया जाएगा? क्या वे कंटेनर में वेक्टर की सामग्री को स्थानांतरित नहीं करेंगे और ऐसा करके उन्हें मूल वेक्टर से हटा दें? – ghord

+0

@Mankarse: मुझे यह नियम के अपवाद के रूप में नहीं दिख रहा है क्योंकि नियम पहले से शुरू होता है "यदि आप अपनी प्रतिलिपि चाहते हैं ..."। अगर आपको प्रतिलिपि की आवश्यकता नहीं है, तो एक मत बनाओ। यदि आपको एक की जरूरत है, महंगा है या नहीं होने वाला है। – GManNickG

3

आप के लिए इस टेम्पलेट चाल-सक्षम वर्ग द्वारा लिखे गए। इसका अध्ययन करें और आपको यह मिल जाएगा।

/// <summary>Container.</summary> 
class Container { 
private: 
    // Here be data! 
    std::vector<unsigned char> _Bytes; 

public: 
    /// <summary>Default constructor.</summary> 
    Container(){ 
    } 

    /// <summary>Copy constructor.</summary> 
    Container(const Container& Copy){ 
     *this = Copy; 
    } 

    /// <summary>Copy assignment</summary> 
    Container& operator = (const Container& Copy){ 
     // Avoid self assignment 
     if(&Copy == this){ 
      return *this; 
     } 
     // Get copying 
     _Bytes = Copy._Bytes; // Copies _Bytes 
     return *this; 
    } 

    /// <summary>Move constructor</summary> 
    Container(Container&& Move){ 
     // You must do this to pass to move assignment 
     *this = std::move(Move); // <- Important 
    } 

    /// <summary>Move assignment</summary> 
    Container& operator = (Container&& Move){ 
     // Avoid self assignment 
     if(&Move == this){ 
      return *this; 
     } 
     // Get moving 
     std::swap(_Bytes, Move._Bytes); // Moves _Bytes 
     return *this; 
    } 
}; // class Container 

मैं इस तरह मूल्य तर्कों का उपयोग के खिलाफ हमेशा हूँ:

function(std:vector<item2> items) 

मैं हमेशा या तो का उपयोग करें:

function(const std:vector<item2>& items) 
function(std:vector<item2>& items) 
function(std:vector<item2>&& items) 

विशेष रूप से बड़े डेटा कंटेनर, और शायद ही कभी के लिए:

function(std:vector<item2> items) 

छोटे दा के लिए टा, कभी वैक्टर नहीं।

इस तरह, आप क्या होता है इसके नियंत्रण में हैं और यही कारण है कि हम सबकुछ नियंत्रित करने के लिए सी ++ करते हैं।

  • यदि आपको एक लिखने योग्य प्रति की आवश्यकता है, तो बस एक चर में कॉन्स्ट संदर्भ को कॉपी करें।
  • यदि आपको इसे केवल पढ़ने की आवश्यकता है, तो कॉन्स्ट संदर्भ एक नई प्रति को रोकता है।
  • यदि आप मूल संपादित करना चाहते हैं, तो बस संदर्भ का उपयोग करें।
  • और आलसी महसूस करते समय छोटे डेटा के लिए मूल्य तर्क का उपयोग करें।

जाहिर है, यह सब आप जो कर रहे हैं उस पर निर्भर करता है।

मैं स्वयं को सी ++ डेवलपर सिखाया गया हूं। एक विशेषज्ञ से बहुत दूर, खासकर सी ++ स्लैंग में ... लेकिन सीखना :)

+1

[सी ++ 11 में कुशल तर्क पासिंग] (http://www.codesynthesis.com/~boris/blog/2012/06/26/efficient-argument-passing-cxx11-part2/) ... अच्छी व्याख्या। – CodeAngry

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