벡터의 요소를 지우면 작동하지 않습니다


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

반복자를 1- 파라미터 과부하 로 전달하는 것은 정의되지 않은 동작 입니다. 그렇지 않은 경우에도 지정된 요소의 "at and after"반복자를 무효화 하여 첫 번째 루프 반복 후에 무효화합니다 .end()erase()erase()d

std::vectorerase()제거 할 다양한 요소를 허용 하는 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()A는 LegacyRandomAccessIterator 유효하도록 지정되어 -다른 반환 작업 LegacyRandomAccessIterator을 .


2

end()from cppreference 의 정의 는 다음과 같습니다.

벡터 컨테이너의 과거 요소를 참조하는 반복자를 반환합니다.

약간 아래 :

그것은 어떤 요소도 가리 키지 않으므로 역 참조되지 않아야한다.

즉, 벡터에는 end ()가 가리키는 요소가 없습니다. erase () 메소드를 통해 요소아닌 요소 를 역 참조 하면 벡터에 속하지 않는 메모리가 변경 될 수 있습니다. 따라서 추악한 일이 발생할 수 있습니다.

간격에 "낮음"값이 포함 되고 간격에서 제외 된 "높음"값과 함께 간격을 [낮음, 높음)으로 설명하는 것이 일반적인 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

질문에 대한 의견 (현재 삭제됨)은 "반복자를위한 연산자가 없습니다"라고 말했습니다. 그러나 다음 코드 는 표준을 또는로 설정하여 and 에서 모두 컴파일 하고 작동 합니다 .MSVCclang-clC++17C++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

@PaulMcKenzie 실제로-clang 정적 분석기 (표준에 매우 엄격 할 수 있음)는 그것에 대해 경고하지 않았습니다.
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.