การลบ () องค์ประกอบในเวกเตอร์ไม่ทำงาน


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 รายการสุดท้ายนั้นเป็นเรื่องง่าย - เพียงใช้pop_back 3 ครั้ง:

#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

มันเป็นพฤติกรรมที่ไม่ได้กำหนดไว้เพื่อส่งผ่านตัวend()วนซ้ำไปยังerase()โอเวอร์โหลด1 พารามิเตอร์ แม้ว่ามันจะไม่ได้erase()เป็นโมฆะมันเป็นโมฆะที่ "ที่และหลัง" องค์ประกอบที่ระบุทำให้dไม่ถูกต้องหลังจากวนซ้ำที่ 1

std::vectorมีerase()โอเวอร์โหลด2 พารามิเตอร์ที่ยอมรับช่วงขององค์ประกอบที่จะลบ คุณไม่จำเป็นต้องใช้ลูปด้วยตนเองเลย:

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

คำจำกัดความของend()จากcppreferenceคือ:

ส่งคืนตัววนซ้ำหมายถึงองค์ประกอบที่ผ่านมาในภาชนะเวกเตอร์

และด้านล่างเล็กน้อย:

มันไม่ได้ชี้ไปที่องค์ประกอบใด ๆ และดังนั้นจะไม่ถูกยกเลิกการลงทะเบียน

กล่าวอีกนัยหนึ่งเวกเตอร์ไม่มีองค์ประกอบที่จุดสิ้นสุด () ชี้ไปที่ โดยการพิจารณาว่าไม่ใช่องค์ประกอบผ่านวิธีการลบ () คุณอาจจะเปลี่ยนหน่วยความจำที่ไม่ได้เป็นของเวกเตอร์ ดังนั้นสิ่งที่น่าเกลียดสามารถเกิดขึ้นได้จากที่นั่น

เป็นแบบแผน 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 ritvector Xเริ่มต้นที่องค์ประกอบสุดท้ายของ rbeginตำแหน่งนี้จะเรียกว่า
  • eraseต้องใช้คลาสสิกiteratorในการทำงานกับ เราได้รับจากที่โดยการเรียก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;
    }

อย่างไรก็ตามฉันไม่มีทนายความด้านภาษาซีพลัสพลัสและเป็นไปได้ว่านี่เป็นส่วนขยาย Microsoft หนึ่งในรายการ 'อันตราย' ฉันจะสนใจเป็นอย่างยิ่งที่จะทราบว่าสิ่งนี้ใช้ได้กับแพลตฟอร์ม / คอมไพเลอร์อื่น ๆ หรือไม่


2
ฉันคิดว่ามันถูกต้องเนื่องจากตัววนซ้ำของเวกเตอร์เป็นการเข้าถึงแบบสุ่มและ-ถูกกำหนดไว้สำหรับตัววนซ้ำชนิดนั้น
PaulMcKenzie

@PaulMcKenzie อันที่จริง - ตัววิเคราะห์แบบคงที่เสียงดังกราว (ซึ่งค่อนข้างเข้มงวดกับมาตรฐาน) ไม่ได้เตือนอะไรเลย
Adrian Mole

1
แม้ว่าจะไม่ได้operator-กำหนดไว้สำหรับตัววนซ้ำคุณสามารถใช้std::advance()หรือstd::prev()แทน
Remy Lebeau

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 
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.