2010-02-16 14 views
115

वालग्रिंड के साथ कई जांच के बाद, मैंने निष्कर्ष निकाला है कि std :: वेक्टर उस ऑब्जेक्ट की एक प्रति बनाता है जिसे आप push_back करना चाहते हैं।क्या स्टडी :: वेक्टर पुश_बैक के साथ ऑब्जेक्ट्स की प्रतिलिपि बना रहा है?

क्या यह वाकई सच है? एक वेक्टर बिना किसी प्रतिलिपि के किसी ऑब्जेक्ट का संदर्भ या पॉइंटर रख सकता है?

धन्यवाद

+15

यह सी के एक बुनियादी सिद्धांत है ++। वस्तुएं मूल्य हैं। असाइनमेंट एक प्रति बनाता है। एक ही ऑब्जेक्ट का जिक्र करने वाले दो चर संभव नहीं हैं जब तक कि आप पॉइंटर या संदर्भ बनाने के लिए '*' या '&' के साथ प्रकार को संशोधित न करें। –

+4

@DanielEarwicker push_back वास्तव में एक संदर्भ लेता है। यह अकेले हस्ताक्षर से स्पष्ट नहीं है कि यह एक प्रतिलिपि बनायेगा। –

+2

@ ब्रायन गॉर्डन - यह नहीं कह रहा है! इसलिए मार्गदर्शक सिद्धांत की आवश्यकता है। फिर भी, हम 'push_back' के हस्ताक्षर से कुछ घटा सकते हैं: इसमें 'const &' लेता है। या तो यह मूल्य दूर (बेकार) फेंकता है, या एक पुनर्प्राप्ति विधि है। इसलिए हम 'बैक' के हस्ताक्षर को देखते हैं, और यह सादा '&' देता है, इसलिए मूल मूल्य की प्रतिलिपि बनाई गई थी या 'कॉन्स्ट' को चुपचाप दूर कर दिया गया है (बहुत बुरा: संभावित रूप से अपरिभाषित व्यवहार)। तो मानते हैं कि 'वेक्टर' के डिजाइनर तर्कसंगत थे ('वेक्टर ' नहीं) हम निष्कर्ष निकालते हैं कि यह प्रतियां बनाता है। –

उत्तर

124

हां, std::vector<T>::push_back() तर्क की एक प्रति बनाता है और इसे वेक्टर में संग्रहीत करता है। यदि आप अपने वेक्टर में ऑब्जेक्ट्स को पॉइंटर्स स्टोर करना चाहते हैं, तो std::vector<whatever> के बजाय std::vector<whatever*> बनाएं।

हालांकि, आपको यह सुनिश्चित करने की ज़रूरत है कि पॉइंटर्स द्वारा संदर्भित ऑब्जेक्ट्स वैध रहें जबकि वेक्टर उनके संदर्भ में हों (आरएआईआई मुहावरे का उपयोग करने वाले स्मार्ट पॉइंटर्स समस्या को हल करते हैं)।

+5

+1 मैं यह भी ध्यान दूंगा कि, यदि आप कच्चे पॉइंटर्स का उपयोग करते हैं, तो अब आप उनके बाद सफाई के लिए ज़िम्मेदार हैं। ऐसा करने का कोई अच्छा कारण नहीं है (जिसे मैं वैसे भी सोच सकता हूं), आपको हमेशा एक स्मार्ट पॉइंटर का उपयोग करना चाहिए। –

+0

की आवश्यकता का जिक्र करने के लिए –

+1

ने कहा कि, आपको अधिक जानकारी के लिए, stl कंटेनरों में std :: auto_ptr का उपयोग नहीं करना चाहिए: [क्यों-यह-गलत-उपयोग-st -auto-ptr-with-standard-containers] (http: // stackoverflow। कॉम/प्रश्न/111478/क्यों-यह-गलत-उपयोग-उपयोग-stdauto-ptr-with-standard-containers) – OriginalCliche

32

हाँ, std::vector भंडार प्रतियां। vector को कैसे पता होना चाहिए कि आपकी वस्तुओं के अपेक्षित जीवन-काल क्या हैं?

आप स्थानांतरित या वस्तुओं के स्वामित्व साझा करना चाहते हैं संकेत दिए गए, shared_ptr (TR1Boost या में पाया) की तरह संभवतः स्मार्ट संकेत का उपयोग संसाधन प्रबंधन को कम करने।

+2

shared_ptr का उपयोग करना सीखें - वे वही करते हैं जो आप चाहते हैं। मेरा पसंदीदा मुहावरे टाइपिफ बूस्ट है :: shared_ptr FooPtr; फिर FooPtrs – pm100

+3

@ pm100 के कंटेनर बनाएं - क्या आपको 'boost :: ptr_vector' पता है? – Manuel

+0

http://www.boost.org/doc/libs/1_42_0/libs/ptr_container/doc/ptr_vector.html के अनुसार, यह एक कच्चा सूचक है, स्मार्ट नहीं। खतरनाक। –

13

std :: वेक्टर हमेशा वेक्टर में जो कुछ भी संग्रहीत किया जा रहा है उसकी एक प्रति बनाता है।

यदि आप पॉइंटर्स का वेक्टर रखते हैं, तो यह पॉइंटर की प्रतिलिपि बनायेगा, लेकिन उदाहरण नहीं है कि पॉइंटर इंगित कर रहा है। यदि आप बड़ी वस्तुओं से निपट रहे हैं, तो आप हमेशा (और शायद चाहिए) पॉइंटर्स के वेक्टर का उपयोग कर सकते हैं। अक्सर, उचित उद्देश्यों के स्मार्ट पॉइंटर्स के वेक्टर का उपयोग सुरक्षा उद्देश्यों के लिए अच्छा होता है, क्योंकि ऑब्जेक्ट आजीवन और मेमोरी प्रबंधन को संभालना अन्यथा मुश्किल हो सकता है।

+3

यह इस प्रकार पर निर्भर नहीं है। यह हमेशा एक प्रति बनाता है। यदि यह सूचक पॉइंटर – pm100

+0

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

+1

हां, यह हमेशा प्रतिलिपि बना रहा है - हालांकि, "ऑब्जेक्ट" ओपी का जिक्र करना संभवतः एक वर्ग या संरचना है, इसलिए मैं इस बात का जिक्र कर रहा था कि यह "ऑब्जेक्ट" की प्रतिलिपि बना रहा है या नहीं। हालांकि, खराब शब्द।स्मार्ट पॉइंटर्स –

3

न केवल std :: वेक्टर जो कुछ भी आप दबा रहे हैं उसकी एक प्रति बनाते हैं, लेकिन संग्रह की परिभाषा बताती है कि यह ऐसा करेगा, और आप वेक्टर के भीतर सही प्रति semantics के बिना वस्तुओं का उपयोग नहीं कर सकते । तो, उदाहरण के लिए, आप एक वेक्टर में auto_ptr का उपयोग नहीं करते हैं।

0

यदि आप प्रतियां नहीं चाहते हैं; तो सबसे अच्छा तरीका एक पॉइंटर वेक्टर (या एक और संरचना जो एक ही लक्ष्य के लिए कार्य करता है) का उपयोग करना है। यदि आप प्रतियां चाहते हैं; सीधे push_back() का उपयोग करें। आपके पास कोई अन्य विकल्प नहीं है।

+1

के बारे में एक नोट बनाया सूचक वेक्टर: वेक्टर > वेक्टर से बहुत सुरक्षित है और shared_ptr पिछले वर्ष के रूप में मानक का हिस्सा है। –

-1

यह पता लगाने के लिए बहुत सारी वाल्ग्रिंड जांच क्यों हुई! बस इसे अपने आप को कुछ सरल कोड के साथ साबित करें उदा।

std::vector<std::string> vec; 

{ 
     std::string obj("hello world"); 
     vec.push_pack(obj); 
} 

std::cout << vec[0] << std::endl; 

हैं "हैलो दुनिया" छपा है, वस्तु चाहिए

+3

यह एक सबूत नहीं है। यदि ऑब्जेक्ट की प्रतिलिपि नहीं बनाई गई थी, तो आपका अंतिम विवरण अपरिभाषित व्यवहार होगा और _could_ प्रिंट हैलो। – Mat

+4

सही परीक्षण सम्मिलन के बाद दोनों में से एक को संशोधित करेगा। अगर वे एक ही वस्तु थे (यदि वेक्टर एक संदर्भ संग्रहीत), दोनों संशोधित किया जाएगा। –

2

प्रासंगिक सी ++ 11 में कॉपी किया गया है सदस्य काम करता है, जो आप उन्हें ले जाकर वस्तुओं के स्वामित्व का हस्तांतरण करने की अनुमति के emplace परिवार है कंटेनर में

उपयोग के मुहावरे की तरह

std::vector<Object> objs; 

Object l_value_obj { /* initialize */ }; 
// use object here... 

objs.emplace_back(std::move(l_value_obj)); 

लगेगा lvalue वस्तु के लिए कदम अन्यथा के रूप में महत्वपूर्ण है कि यह एक संदर्भ या स्थिरांक संदर्भ और चाल निर्माता के नाम से जाना नहीं होगा, क्योंकि भेजा जाता है है।

11
सी ++ 11 के बाद से, सभी मानक कंटेनर ( std::vector, std::map, आदि) समर्थन चाल अर्थ विज्ञान से

, जिसका अर्थ है कि आप अब मानक कंटेनरों को rvalues ​​गुजरती हैं और एक प्रति बच सकते हैं: वैकल्पिक रूप से आप

// Example object class. 
class object 
{ 
private: 
    int    m_val1; 
    std::string  m_val2; 

public: 
    // Constructor for object class. 
    object(int val1, std::string &&val2) : 
     m_val1(val1), 
     m_val2(std::move(val2)) 
    { 

    } 
}; 

std::vector<object> myList; 

// #1 Copy into the vector. 
object foo1(1, "foo"); 
myList.push_back(foo1); 

// #2 Move into the vector (no copy). 
object foo2(1024, "bar"); 
myList.push_back(std::move(foo2)); 

// #3 Move temporary into vector (no copy). 
myList.push_back(object(453, "baz")); 

// #4 Create instance of object directly inside the vector (no copy, no move). 
myList.emplace_back(453, "qux"); 

ज्यादातर एक ही प्रभाव प्राप्त करने के लिए विभिन्न स्मार्ट संकेत का उपयोग कर सकते हैं:

std::unique_ptr उदाहरण

std::vector<std::unique_ptr<object>> myPtrList; 

// #5a unique_ptr can only ever be moved. 
auto pFoo = std::make_unique<object>(1, "foo"); 
myPtrList.push_back(std::move(pFoo)); 

// #5b unique_ptr can only ever be moved. 
myPtrList.push_back(std::make_unique<object>(1, "foo")); 

std::shared_ptr उदाहरण

std::vector<std::shared_ptr<object>> objectPtrList2; 

// #6 shared_ptr can be used to retain a copy of the pointer and update both the vector 
// value and the local copy simultaneously. 
auto pFooShared = std::make_shared<object>(1, "foo"); 
objectPtrList2.push_back(pFooShared); 
// Pointer to object stored in the vector, but pFooShared is still valid. 
+0

ध्यान दें कि 'std :: make_unique' (परेशान) केवल C++ 14 और उसके बाद में उपलब्ध है। यदि आप इन उदाहरणों को संकलित करना चाहते हैं तो सुनिश्चित करें कि आप अपने कंपाइलर को इसके अनुरूप अनुरूपता निर्धारित करने के लिए बताएं। –

+0

5 ए में आप पुनरावृत्ति से बचने के लिए 'auto pFoo =' का उपयोग कर सकते हैं; और सभी 'std :: string' casts को हटाया जा सकता है (स्ट्रिंग अक्षर से 'std :: string' तक अंतर्निहित रूपांतरण है) –

+1

@ user465139' make_unique' आसानी से C++ 11 में कार्यान्वित किया जा सकता है, इसलिए यह केवल एक है सी ++ 11 कंपाइलर –