मिटाना () वेक्टर में एक तत्व काम नहीं करता है


10

मेरे पास एक वेक्टर है। मुझे इसमें अंतिम 3 तत्वों को हटाने की आवश्यकता है। इस तर्क का वर्णन किया। प्रोग्राम क्रैश हो जाता है। क्या गलती हो सकती है?

vector<float>::iterator d = X.end();
    for (size_t i = 1; i < 3; i++) {
        if (i == 1) X.erase(d);
        else X.erase(d - i);
    }

यहाँ हत्यारा dवास्तव में मौजूद नहीं है। यह केवल एक का पिछला-अंत-का-कैनरी मूल्य है जिसका अंत केवल खोजने योग्य है vector। आप इसे हटा नहीं सकते। अगला, जैसे ही आप एक पुनरावृत्ति मिटाते हैं, यह चला गया है। आप किसी भी चीज के लिए बाद में सुरक्षित रूप से इसका उपयोग नहीं कर सकते हैं, जिसमें शामिल हैं d - i
user4581301

जवाबों:


9

यदि वेक्टर में कम से कम 3 आइटम हैं, तो अंतिम 3 आइटम को हटाना सरल है - बस 3 बार pop_back का उपयोग करें :

#include <vector>
#include <iostream>

int main() 
{
    std::vector<float> v = { 1, 2, 3, 4, 5 };
    for (int i = 0; i < 3 && !v.empty(); ++i)
       v.pop_back();

    for ( const auto &item : v ) std::cout << item << ' ';
        std::cout << '\n';
}

आउटपुट:

1 2

11

1-पैरामीटर अधिभार के लिए पुनरावृत्ति पास करने के लिए यह अपरिभाषित व्यवहार है । यहां तक ​​कि अगर यह नहीं थे, तो पुनरावृत्तियों को अमान्य करता है जो निर्दिष्ट तत्व पर "पहले और बाद में" हैं, 1 लूप पुनरावृत्ति के बाद अमान्य बना रहा है ।end()erase()erase()d

std::vector2-पैरामीटर erase()अधिभार है जो हटाने के लिए तत्वों की एक श्रृंखला को स्वीकार करता है। आपको मैन्युअल लूप की आवश्यकता नहीं है:

if (X.size() >= 3)
    X.erase(X.end()-3, X.end());

लाइव डेमो


3

सबसे पहले, X.end()वेक्टर के अंतिम तत्व के लिए एक पुनरावृत्ति वापस नहीं करता है, यह बल्कि वेक्टर के अंतिम तत्व के पिछले तत्व के लिए एक पुनरावृत्त लौटाता है, जो एक ऐसा तत्व है जो वेक्टर वास्तव में स्वयं का नहीं होता है, इसीलिए जब आप प्रयास करते हैं इसे X.erase(d)प्रोग्राम क्रैश के साथ मिटा दें ।

इसके बजाय, बशर्ते कि वेक्टर में कम से कम 3 तत्व हों, आप निम्न कार्य कर सकते हैं:

X.erase( X.end() - 3, X.end() );

जो बदले में तीसरे अंतिम तत्व तक जाता है, और उसके बाद हर तत्व को मिटा देता है X.end()

संपादित करें: केवल स्पष्ट करने के लिए, X.end()एक LegacyRandomAccessIterator है जो एक मान्य -ऑपरेशन के लिए निर्दिष्ट है जो एक अन्य LegacyRandomAccessIterator देता है ।


2

Cppreferenceend() से परिभाषा है:

वेक्टर कंटेनर में अतीत के अंत तत्व का जिक्र करते हुए एक पुनरावृत्ति देता है।

और थोड़ा नीचे:

यह किसी भी तत्व की ओर इशारा नहीं करता है, और इस तरह से इसे समाप्त नहीं किया जाएगा।

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

अंतराल में शामिल "निम्न" मान और अंतराल से बाहर रखा गया "उच्च" मान के साथ, अंतराल [निम्न, उच्च) के रूप में वर्णन करना सामान्य C ++ सम्मेलन है ।


2

आप एक का उपयोग कर सकते हैं reverse_iterator:

#include <iostream>
#include <vector>

using namespace std;

int main()
{
    vector<float> X = {1.1, 2.2, 3.3, 4.4, 5.5, 6.6};

    // start the iterator at the last element
    vector<float>::reverse_iterator rit = X.rbegin();

    // repeat 3 times
    for(size_t i = 0; i < 3; i++)
    {
        rit++;
        X.erase(rit.base());
    }

    // display all elements in vector X
    for(float &e: X)
        cout << e << '\n';

    return 0;
}

उल्लेख करने के लिए कुछ चीजें हैं:

  • reverse_iterator ritके अंतिम तत्व पर शुरू होता है vector X। इस स्थिति को कहा जाता है rbegin
  • eraseiteratorसाथ काम करने के लिए क्लासिक की आवश्यकता होती है । हमें वह ritकॉल करने से मिलता है base। लेकिन वह नया पुनरावृत्ति ritआगे के दिशा से अगले तत्व को इंगित करेगा ।
  • इसलिए हम ritकॉल करने से पहले अग्रिम baseऔरerase

यदि आप इसके बारे में अधिक जानना चाहते हैं reverse_iterator, तो मैं इस उत्तर पर जाने का सुझाव देता हूं ।


2

प्रश्न में एक टिप्पणी (अब हटा दी गई) में कहा गया है कि "इट्रेटर के लिए कोई संचालक नहीं है।" हालाँकि, निम्न कोड कंपाइल करता है और दोनों में काम करता है MSVCऔर clang-cl, C++17या तो मानक सेट के साथ या C++14:

#include <iostream>
#include <vector>

int main()
{
    std::vector<float> X{ 1.1f, 2.2f, 3.3f, 4.4f, 5.5f, 6.6f };
    for (auto f : X) std::cout << f << ' '; std::cout << std::endl;
    std::vector<float>::iterator d = X.end();
    X.erase(d - 3, d);  // This strongly suggest that there IS a "-" operator for a vector iterator!
    for (auto f : X) std::cout << f << ' '; std::cout << std::endl;
    return 0;
}

इस के लिए प्रदान की गई परिभाषा operator-इस प्रकार है ( <vector>हेडर में):

    _NODISCARD _Vector_iterator operator-(const difference_type _Off) const {
        _Vector_iterator _Tmp = *this;
        return _Tmp -= _Off;
    }

हालाँकि, मैं निश्चित रूप से कोई C ++ भाषा-वकील नहीं हूँ, और यह संभव है कि यह उन 'खतरनाक' Microsoft एक्सटेंशनों में से एक है। मुझे यह जानने में बहुत दिलचस्पी होगी कि क्या यह अन्य प्लेटफार्मों / कंपाइलरों पर काम करता है।


2
मुझे लगता है कि यह वैध है, क्योंकि वेक्टर के पुनरावृत्तियों यादृच्छिक पहुँच हैं, और -उन प्रकार के पुनरावृत्तियों के लिए परिभाषित किया गया है।
पॉलमैकेन्जी

@PaulMcKenzie वास्तव में - क्लैंग स्टैटिक एनालाइज़र (जो मानकों के साथ बहुत सख्त हो सकता है) ने इसके बारे में कोई चेतावनी नहीं दी।
एड्रियन मोल

1
यहां तक ​​कि अगर operator-पुनरावृत्तियों के लिए कोई परिभाषित नहीं किया गया था, तो आप बस std::advance()या std::prev()इसके बजाय उपयोग कर सकते हैं ।
रेमी लेबेउ

1

यह कथन

    if (i == 1) X.erase(d);

अपरिभाषित व्यवहार है।

और यह कथन अंतिम तत्व से पहले केवल तत्व को हटाने की कोशिश करता है

    else X.erase(d - i);

क्योंकि आपके पास केवल दो पुनरावृत्तियों के साथ एक लूप है

for (size_t i = 1; i < 3; i++) {

आपको निम्नलिखित की तरह कुछ चाहिए।

#include <iostream>
#include <vector>
#include <iterator>
#include <algorithm>

int main() 
{
    std::vector<float> v = { 1, 2, 3, 4, 5 };

    auto n = std::min<decltype( v.size() )>( v.size(), 3 ); 
    if ( n ) v.erase( std::prev( std::end( v ), n ), std::end( v ) );

    for ( const auto &item : v ) std::cout << item << ' ';
    std::cout << '\n';

    return 0;
}

कार्यक्रम का आउटपुट है

1 2 
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.