zoukankan      html  css  js  c++  java
  • C++避坑:避免在循环遍历容器时,在循环体内删除、增加元素

    随着业务的增长,循环体可能会逐渐复杂起来。

    我们通常遍历一个容器,对其中的每一个元素执行方法,从而更新它们的状态。随着代码逐渐复杂,我们在写新的方法时,可能并没有意识到this正处于循环遍历当中。此时若对容器进行大小的修改,即增加元素或删除元素,是一个危险的行为。

    对于 std::vector ,如果这样写:

    std::vector<Foo> vfoo;
    // ...
    for (auto it = vfoo.begin(); it != vfoo.end(); ++it)
    {
    	// do something
    	// add Foo to vector
    }
    

    由于对vector增加元素可能导致重新分配容器内存,因此分配之后旧的迭代器会失效,此时it不能再使用,否则会有未定义的行为。而删除it所指的对象同样会导致it失效。

    即便是对于原生数组,我们也要注意。假如我们有这样的数组元素:

    Foo *foo = new Foo;
    foo_list[index] = foo;
    

    foo_list 管理着动态分配的Foo对象,现在遍历它:

    for (int i = 0; i < MAX_ITEM_COUNT; ++i)
    {
    	foo_list[i]->Update();
    }
    

    在Foo::Update中有这样的代码:

    void Foo::Update()
    {
    	if (...) m_foo_list->Remove(key);
    
    	this->OtherMemberFunction();
    }
    

    m_foo_list->Remove(key); 令遍历它的容器foo_list delete掉某一个对象,这个对象可能是别的对象,也可能是this对象,而接下来,访问其成员则是未定义的行为。

    在成员函数中把this删掉看起来比较奇怪,但的确有可能发生。

    我们避免在循环中错误地修改容器的一个解决方案就是,延迟删除,即设置一个标记,在循环开始或结束的时候,对设置标记的元素进行统一删除处理。

    或者,对于简单的遍历操作,可以采用如下惯用法删除:

    std::map<key, value> kvmap;
    for (auto it = kvmap.begin(); it != kvmap.end(); )
    {
    	if (...)
    	{
    		// ...
    		it = kvmap.erase(it);
    	}
    	else
    	{
    		++it;
    	}
    }
    
  • 相关阅读:
    Solaris下批量kill掉oracle的session
    我写blog的方式变迁
    filezilla ftp client在win7 下获取ftp目录信息出错解决方法
    GNU System Monitor编译问题
    在vmware的Solaris虚拟机中安装vmtool
    关于golden gate director client的一点点使用总结
    测试 乱弹
    ORM的世界 (再补充)
    Yahoo Konfabulator
    轻量容器和注射依赖 的自实现
  • 原文地址:https://www.cnblogs.com/demon90s/p/15689677.html
Copyright © 2011-2022 走看看