c++ 如何在不改变 end() 的情况下擦除元素

Posted

技术标签:

【中文标题】c++ 如何在不改变 end() 的情况下擦除元素【英文标题】:c++ How to erase an element without changing its end() 【发布时间】:2019-12-25 02:08:33 【问题描述】:

我正在尝试按以下索引从向量中删除一个元素,但为什么第一个输出与第二个输出不同?有什么方法可以避免这种情况,或者有更好的删除元素的方法吗?

int main() 
    vector<int> test = 1, 2, 3, 4, 5, 6, 7, 8, 9, 10;
    cout << *test.end() << endl;
    test.erase(test.begin() + 2);
    cout << *test.end() << endl;
    return 0;

【问题讨论】:

end() 是最后一个元素之后的元素,解引用没有意义 *test.end() 是未定义的行为。 我只是想证明 end 实际上发生了变化,因为有时我可能想比较它们 你不能通过取消引用一个不可引用的迭代器来“证明”任何事情。 “有时我可能想比较它们” 这也没有意义;如果您要在erase 之前缓存test.end() 的结果并在erase 之后使用它,那么您将再次出现未定义的行为(即使使用==),因为迭代器将失效。你的真正问题是什么? 【参考方案1】:

std::vector::end将迭代器返回到元素在最后一个元素之后,对它的解引用导致UB,意味着一切皆有可能,只是没有意义。

返回一个迭代器,指向容器最后一个元素之后的元素。

此元素充当占位符;尝试访问它会导致未定义的行为。

您可以使用std::vector::back 来获取最后一个元素; (最好提前检查vector是否为空。)

int main() 
    vector<int> test = 1, 2, 3, 4, 5, 6, 7, 8, 9, 10;
    cout << test.back() << endl;
    test.erase(test.begin() + 2);
    cout << test.back() << endl;
    return 0;

【讨论】:

但是我擦除元素后,end()变成了10,这是定义的,我该如何避免呢? @WhalalalalalalalaCHen end() not “变成 10”。停止取消引用它。你不能那样做。 @WhalalalalalalaCHen 只是不要取消对具有 UB 的 end 的引用。你想避免什么? 我想通过 find(test.begin(), test.end(), element) == test.end() 来检查一个元素是否存在 那就这样做吧。这与您在问题中提出的内容或您在 cmets 中不断说的话有什么关系?【参考方案2】:

通常你不能打印 *test.end() ,因为它不是最后一个指针,它在最后一个指针之后。它的值对你无效,可能会导致异常。

vector::end() == vector::begin() + vector::size()

你可以使用vector::back

我检查了vector::erase()的stl代码

    iterator erase(const_iterator _Where) noexcept(is_nothrow_move_assignable_v<value_type>) /* strengthened */ 
        const pointer _Whereptr = _Where._Ptr;
        auto& _My_data          = _Mypair._Myval2;
        pointer& _Mylast        = _My_data._Mylast;

#if _ITERATOR_DEBUG_LEVEL == 2
        _STL_VERIFY(
            _Where._Getcont() == _STD addressof(_My_data) && _Whereptr >= _My_data._Myfirst && _Mylast > _Whereptr,
            "vector erase iterator outside range");
        _Orphan_range(_Whereptr, _Mylast);
#endif // _ITERATOR_DEBUG_LEVEL == 2

        _Move_unchecked(_Whereptr + 1, _Mylast, _Whereptr);
        _Alty_traits::destroy(_Getal(), _Unfancy(_Mylast - 1));  // here after move it destroy the last element.
        --_Mylast;
        return iterator(_Whereptr, _STD addressof(_My_data));
    

所以使用 *end() 可能会导致野指针异常。

【讨论】:

*test.end() 既不能正常也不能异常,你不需要检查代码,文档就足够了。它不是指针而是迭代器。【参考方案3】:

尝试列表

 #include <vector>
 #include <list>
   int main() 
    list<int> test = 1, 2, 3, 4, 5, 6, 7, 8, 9, 10;

    auto pbeg=test.begin(),
    pend=test.end(),
    a = pbeg, b = pend;
    cout << *(--b) << endl;
    ++(++a);
    test.erase(a);
    cout << *(--b) << endl;
    for ( a=pbeg; a!=pend; ++a)
        cout<< *a <<" ";

    return 0;

10
10
1 2 4 5 6 7 8 9 10

【讨论】:

【参考方案4】:

如果您想在从容器中擦除或移除项目期间避免迭代器失效,则需要重新访问您对容器的选择。

下面的链接可以提供迭代器失效的概述:

http://kera.name/articles/2011/06/iterator-invalidation-rules-c0x/

【讨论】:

以上是关于c++ 如何在不改变 end() 的情况下擦除元素的主要内容,如果未能解决你的问题,请参考以下文章

c++如何在不改变输入顺序的情况下去重输出?

你知道如何在不改变其他元素顺序的情况下将元素移动到第一个位置吗?

如何在不改变坐标的情况下转换(旋转)svg文本元素?

如何在不等待 C++ 响应的情况下启动线程?

为什么对话框会在不刷新的情况下删除其内容

尝试擦除最后一个std :: vector元素时程序崩溃