एसटीडी :: वेक्टर एक push_back के साथ वस्तुओं की नकल कर रहा है?


169

Valgrind के साथ बहुत सारी जांच के बाद, मैंने निष्कर्ष निकाला है कि std :: वेक्टर एक ऑब्जेक्ट की एक प्रतिलिपि बनाता है जिसे आप push_back करना चाहते हैं।

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

धन्यवाद


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

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

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

जवाबों:


183

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

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


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

1
उस ने कहा, आपको stl कंटेनरों में std :: auto_ptr का उपयोग नहीं करना चाहिए, अधिक जानकारी के लिए: Why-is-it-is-wrong-to-use-stdauto-ptr-with-standard-कंटेनर
OriginalCliche

24
C ++ 11 के बाद से, push_backएक प्रति के बजाय एक चाल प्रदर्शन करेंगे यदि तर्क एक संदर्भ है। (वस्तुओं को संदर्भों के साथ परिवर्तित करने के लिए परिवर्तित किया जा सकता है std::move()।)
इमलाई

2
@tuple_cat आपकी टिप्पणी "यदि तर्क एक तर्क है" तो कहना चाहिए। (यदि तर्क एक रैव्यू संदर्भ के रूप में घोषित इकाई का नाम है, तो तर्क वास्तव में एक लवल्यू है और इससे स्थानांतरित नहीं किया जाएगा) - "कार्ल निकोल" के उत्तर के लिए मेरे संपादन की जांच करें जिसने शुरुआत में वह गलती की थी
एमएम

नीचे एक उत्तर दिया गया है, लेकिन यह स्पष्ट करने के लिए: चूंकि C ++ 11 emplace_backकिसी भी प्रतिलिपि या चाल से बचने के लिए उपयोग करता है (कंटेनर द्वारा प्रदान की गई जगह में ऑब्जेक्ट का निर्माण करें)।
कृमि

34

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

यदि आप ऑब्जेक्ट्स के स्वामित्व को स्थानांतरित करना या साझा करना चाहते हैं, तो संभवतः संसाधन प्रबंधन को आसान बनाने के लिए स्मार्ट पॉइंटर्स जैसे shared_ptr( बूस्ट या TR1 में पाया गया )।


3
share_ptr का उपयोग करना सीखें - वे वही करते हैं जो आप चाहते हैं। मेरा पसंदीदा मुहावरा टाइप्डेफ़ को बढ़ावा देना है :: साझा_प्रित <Foo> FooPtr; फिर
pm100

3
@ pm100 - क्या आप जानते हैं boost::ptr_vector?
मैनुअल

2
मैं भी class Foo { typedef boost::shared_ptr<Foo> ptr; };सिर्फ लिखने के लिए उपयोग करना पसंद करता हूं Foo::ptr
रूपर्ट जोन्स

2
@ pm100 - shared_ptrबिल्कुल आग और भूल नहीं है। देखें stackoverflow.com/questions/327573 और stackoverflow.com/questions/701456
डैनियल ईयरविक्सर

2
अगर आपने स्वामित्व साझा किया है, तो share_ptr अच्छा है, लेकिन आमतौर पर इसका अत्यधिक उपयोग होता है। जब स्वामित्व स्पष्ट हो, तो अनूठे या बढ़ावा देने वाले scoped_ptr ज्यादा मायने रखते हैं।
नेमेनजा ट्रिफ़ुनोविक

28

C ++ 11 के बाद से, सभी मानक कंटेनर ( std::vector, std::mapआदि) मूवमेंट शब्दार्थ का समर्थन करते हैं, जिसका अर्थ है कि अब आप मानक कंटेनरों के अंतरालों को पास कर सकते हैं और कॉपी से बच सकते हैं:

// 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.

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

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

2
@ user465139 make_uniqueको C ++ 11 में आसानी से लागू किया जा सकता है, इसलिए यह केवल C ++ 11 संकलक के साथ अटके किसी व्यक्ति के लिए थोड़ी सी नाराज़गी है
MM

1
@MM: वास्तव में। यहाँ पाठ्यपुस्तक कार्यान्वयन है:template<typename T, typename... Args> unique_ptr<T> make_unique(Args&&... args) { return unique_ptr<T>{new T{args...}}; }
लैरीक्स डेसीडुआ

1
@ अचनाक - हां उन्हें करना चाहिए, लेकिन केवल तभी जब आप कॉपी करें। यदि आप के std::move()साथ उपयोग करते हैं std::shared_ptr, तो मूल साझा सूचक इसका सूचक हो सकता है क्योंकि स्वामित्व वेक्टर में पारित हो गया था। यहाँ देखें: coliru.stacked-crooked.com/a/99d4f04f05e5c7f3
कार्ल निकोल

15

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

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


3
यह प्रकार पर निर्भर नहीं करता है। यह हमेशा एक प्रति बनाता है। अगर इसका पॉइंटर पॉइंटर की कॉपी बनाता है
पीएम 100

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

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

3

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


2

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

उपयोग का मुहावरा कैसा लगेगा

std::vector<Object> objs;

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

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

लैवल्यू ऑब्जेक्ट के लिए कदम महत्वपूर्ण है क्योंकि इसे एक संदर्भ या कॉन्स्ट रेफरेंस के रूप में अग्रेषित किया जाएगा और मूव कंस्ट्रक्टर को नहीं बुलाया जाएगा।


0

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


1
पॉइंटर वैक्टर के बारे में एक नोट: वेक्टर <share_ptr <obj>> वेक्टर से अधिक सुरक्षित है <obj *> और साझा किया गया पिछले वर्ष की तरह मानक का हिस्सा है।
अमीर। 7

-1

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

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

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

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

यदि "हैलो वर्ल्ड" मुद्रित है, तो ऑब्जेक्ट की प्रतिलिपि बनाई जानी चाहिए


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

4
सही परीक्षण सम्मिलन के बाद दोनों में से एक को संशोधित करेगा। यदि वे एक ही वस्तु थे (यदि वेक्टर ने एक संदर्भ संग्रहीत किया है), तो दोनों को संशोधित किया जाएगा।
फ्रांसेस्को डोंडी
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.