删除向量中的元素不起作用


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没有杀手really。这是仅可用于找到末尾的金丝雀值vector。您无法删除它。接下来,一旦删除迭代器,它就消失了。之后,您将无法安全地将其用于任何事物,包括d - i
user4581301

Answers:


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参数过载是不确定的行为。即使不是,也会使在指定元素“及之后”的迭代器无效,从而使第1次循环迭代后无效。end()erase()erase()d

std::vector具有2参数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

end()from cppreference的定义是:

返回引用向量容器中的past-the-end元素的迭代器。

并在下面:

它没有指向任何元素,因此不应取消引用。

换句话说,向量没有end()指向的元素。通过使用delete()方法取消引用该非元素,您可能正在更改不属于该向量的内存。因此,丑陋的事情可能从那里发生。

按照通常的C ++惯例,将间隔描述为[low,high],其中“ low”值包含在间隔中,而“ high”值包含在间隔中。


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
  • erase需要经典iterator才能使用。我们可以rit通过致电获得base。但是,新的迭代器将rit向前指向下一个元素。
  • 这就是为什么我们提前rit打电话之前baseerase

另外,如果您想了解更多信息reverse_iterator,建议您访问此答案


2

问题中的注释(现已删除)指出“没有-迭代器的运算符”。但是,以下代码在中都可以编译和工作MSVC并且clang-cl标准设置为C++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确实-c静态分析器(在标准上可能非常严格)没有对此发出警告。
Adrian Mole

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 
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.