STL序列式容器中删除元素的方法和陷阱 一 (转)

本文探讨了在STL标准模板库中,如何安全地从vector和list容器中删除元素,避免常见的陷阱和错误。通过具体示例展示了不同删除方法可能导致的问题,并提供了正确的实现方式。
STL序列式容器中删除元素的方法和陷阱 一 (转)[@more@]

在STL(标准模板库)中经常会碰到要删除容器中部分元素的情况,本人在编程中就经常编写这方面的代码,在编码和测试过程中发现在STL中删除容器有很多陷阱,网上也有不少网友提到如何在STL中安全删除元素这些问题。本文将讨论编程过程中最经常使用的两个序列式容器vector、list中安全删除元素的方法和应该注意的问题,  其它如queue、stack等配接器容器(container adapter),由于它们有专属的操作行为,没有迭代器(iterator),不能采用本文介绍的删除方法,至于deque,它与vector的删除方法一样。STL容器功能强大,but no siliver bullet,如果你使用不当,也将让你吃尽苦头。

1.手工编写for循环代码删除STL序列式容器中元素的方法XML:namespace prefix = o ns = "urn:schemas-microsoft-com:Office:office" />

例如,你能看出以下代码有什么问题?

例1:

#include

#include

using namespace std;

void main( ) {

  vector vectInt;

  int i;

  //  初始化vector容器

  for (i = 0; i < 5; i++ ) {

    vectInt.push_back( i );

  }

  //  以下代码是要删除所有值为4的元素

  vector::iterator itVect = vectInt.begin();

  for ( ; itVect != vectInt.end();  ++itVect ) {

  if ( *itVect == 4 ) {

    vectInt.erase( itVect );

  }

  }

  int iSize = vectInt.size();

  for (  i = 0 ; i < iSize; i++ )  {

    cout << " i= " << i <

  }

 

}

例2:

#include

#include

using namespace std;

void main( ) {

  vector vectInt;

  int i;

  //  初始化vector容器

  for ( i = 0; i < 5; i++ ) {

    vectInt.push_back( i );

  if ( 3 == i ) {

  //  使3的元素有两个,并且相临。这非常关键,否则将发现不了bug

  //  具体解释见下。

    vectInt.push_back( i );

  }

  }

  vector::iterator itVect = vectInt.begin();

  vector::iterator itVectEnd = vectInt.end(); //  防止for多重计算

  //  以下代码是要删除所有值为3的元素

  for ( ; itVect != itVectEnd; ++itVect ) {

  if ( *itVect == 3 ) {

    itVect = vectInt.erase( itVect );

  }

  }

  int iSize = vectInt.size();

  for (  i = 0 ; i < iSize; i++ )  {

    cout << " i= " << i <

  }

例3:

#include

#include

using namespace std;

void main( ) {

  vector vectInt( 5 );

  int i;

  vectInt[ 0 ] = 0;

  vectInt[ 1 ] = 1;

  vectInt[ 2 ] = 2;

  vectInt[ 3 ] = 3;

  vectInt[ 4 ] = 4; //  替换为 vectInt[ 4 ] = 3;试试

  vector::iterator itVect = vectInt.begin();

  vector::iterator itVectEnd = vectInt.end(); //  防止for多重计算

  //  以下代码是要删除所有值为3的元素

  for ( ; itVect != itVectEnd; ) {

  if ( *itVect == 3 ) {

    itVect = vectInt.erase( itVect );

  }

  else {

    ++itVect;

  }

  }

  int iSize = vectInt.size();

  for (  i = 0 ; i < iSize; i++ )  {

    cout << " i= " << i <

  }

}

 

分析:

这里最重要的是要理解erase成员函数,它删除了itVect迭代器指向的元素,Mailto:e4e dateTime=2002-10-28T12:47>并且返回要被删除的itVect之后的迭代器,迭代器相当于一个智能指针,指向容器中的元素,现在删除了这个元素,将导致内存重新分配,相应指向这个元素的迭代器之后的迭代器就失效了,但erase成员函数返回要被删除的itVect之后的迭代器

例1将导致程序未定义的错误,在windows中即是访问非法内存,程序当掉。因为vectInt.erase( itVect );调用后itVect之后的迭代器已无效了,所以当执行++itVect后,*itVect访问了非法内存。例1也是初学者最容易犯的错误,这个错误也比较容易发现。

例2可能会导致不能把vectInt中所有为3的元素删除掉。因为第一次删除成功时,itVect = vectInt.erase( itVect );itVect为指向3之后的位置,之后再执行++itVect,itVect就掉过了被删除元素3之后的元素3,导致只删除了一个为3的元素,这个bug比较隐蔽,因为如果不是两个均为3的元素相临,就将很难捕捉到这个bug,程序有可能在一段时间运行良好,但如碰到容器中两值相同的元素相临,则程序就要出问题。

例3,对于本例你可能要说程序没有任何问题,解决了上面的两个bug,程序也运行正常。但且慢,你把 “vectInt[ 4 ] = 4;” 这一行改为 “vectInt[ 4 ] = 3;”试试,一运行,程序当掉,访问非法内存!你疑惑不解:从程序看不出bug,而且我还把vectInt.end()放在外面计算以防止for多重计算,提高效率。哈哈,问题就出在最后一句话!算法大师Donald Knuth有一句名言:不成熟的优化是一切恶果的根源( Permature optimization is the root of all evil )。由于在for循环中要删除元素,则vectInt.end()是会变化的,所以不能在for循环外计算,而是每删除一次都要重新计算,所以应放在for循环内。那你要问,为什么把 “vectInt[ 4 ] = 4;” 这一行改为 “vectInt[ 4 ] = 3;”程序就会当掉,而不改程序就很正常呢?这就跟vector的实现机制有关了。下面以图例详细解释。

vectInt的初始状态为:

 

        | end

0  1  2  3  4 

 

删除3后,

 

         |新的end  | 原来的end

0   1  2  4  4 

 

 

 

注意上面“新的end”指向的内存并没有被清除,为了效率,vector会申请超过需要的内存保存数据,删除数据时也不会把多余的内存删除。

然后itVect再执行++itVect,因为此时*itVect等于4,所以继续循环, 这时itVect 等于“新的end”但不等于“原来的end”(它即为itVectEnd),所以继续,因为 *itVect访问的是只读内存得到的值为4,不等于3,故不删除,然后执行++itVect此时itVect等于itVectEnd退出循环。从上面过程可以看出,程序多循环了一次(删除几次,就要多循环几次),但程序正常运行。

如果把 “vectInt[ 4 ] = 4;” 这一行改为 “vectInt[ 4 ] = 3;”过程如下:

 

 

          | end

0  1  2  3  3 

 

删除3后,

 

         |新的end  |原来的 end

0  1  2  3  3   

 

 

删除第2个3后,

 

      |新的end    |原来的 end

0  1  2  3  3 

 

这时itVect 等于“新的end”但不等于“原来的end”(它即为itVectEnd),所以继续,因为 *itVect访问的是只读内存得到的值为3,等于3,所以执行删除,但因为*itVect访问的是只读内存不能删除,所以程序当掉。

综上,我们知道当要删除的值在容器末尾时,会导致程序删除非法内存,程序当掉;即使程序正常运行,也是for循环多执行了等于删除个数的循环。所以把vectInt.end()放在for循环外面执行,完全是错误的。对于list容器,list.end()在删除过程中是不会变的,可以把它放在for循环外面计算,但由于list.end()是个常量,把list.end()放在for循环中计算编译器应该可以优化它。从安全考虑,除非你能保证for循环中不会改变容器的大小,否则都应该对容器的值在for循环中计算,对于 vectInt.size()这样的计算,也应该在for循环中计算,不要因为微小的优化而导致程序出错。

 

正确的方法:

例4:

#include

#include

using namespace std;

void main( ) {

  vector vectInt;

  int i;

  for (  i = 0; i < 5; i++ ) {

    vectInt.push_back( i );

  if ( 3 == i ) {

  //  使3的元素有两个,并且相临。

    vectInt.push_back( i );

  }

  }

  vector::iterator itVect = vectInt.begin();

  //  以下代码是要删除所有值为3的元素

  for ( ; itVect != vectInt.end();  ) {  // 删除 ++itVect{

  if ( *itVect == 3 ) {

    itVect = vectInt.erase( itVect );

  }

  else {

    ++itVect;

  }

  }

  //  把vectInt.size()放在for循环中

  for (  i = 0 ; i < vectInt.size(); i++ )  {

    cout << " i= " << i <

  }

运行结果为:

i= 0, 0

i= 1, 1

i= 2, 2

i= 3, 4

从结果显示值为3的元素确实被删除了。

来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/10752043/viewspace-993353/,如需转载,请注明出处,否则将追究法律责任。

转载于:http://blog.itpub.net/10752043/viewspace-993353/

【电力系统】单机无穷大电力系统短路故障暂态稳定Simulink仿真(带说明文档)内容概要:本文档围绕“单机无穷大电力系统短路故障暂态稳定Simulink仿真”展开,提供了完整的仿真模型与说明文档,重点研究电力系统在发生短路故障后的暂态稳定性问题。通过Simulink搭建单机无穷大系统模型,模拟不同类型的短路故障(如三相短路),分析系统在故障期间及切除后的动态响应,包括发电机子角度、速、电压功率等关键参数的变化,进而评估系统的暂态稳定能力。该仿真有助于理解电力系统稳定性机理,掌握暂态过程分析方法。; 适合人群:电气工程及相关专业的本科生、研究生,以及从事电力系统分析、运行与控制工作的科研人员工程师。; 使用场景及目标:①学习电力系统暂态稳定的基本概念与分析方法;②掌握利用Simulink进行电力系统建模与仿真的技能;③研究短路故障对系统稳定性的影响及提高稳定性的措施(如故障清除时间优化);④辅助课程设计、毕业设计或科研项目中的系统仿真验证。; 阅读建议:建议结合电力系统稳定性理论知识进行学习,先理解仿真模型各模块的功能与参数设置,再运行仿真并仔细分析输出结果,尝试改变故障类型或系统参数以观察其对稳定性的影响,从而深化对暂态稳定问题的理解。
本研究聚焦于运用MATLAB平台,将支持向量机(SVM)应用于数据预测任务,并引入粒子群优化(PSO)算法对模型的关键参数进行自动调优。该研究属于机器学习领域的典型实践,其核心在于利用SVM构建分类模型,同时借助PSO的全局搜索能力,高效确定SVM的最优超参数配置,从而显著增强模型的整体预测效能。 支持向量机作为种经典的监督学习方法,其基本原理是通过在高维特征空间中构造个具有最大间隔的决策边界,以实现对样本数据的分类或回归分析。该算法擅长处理小规模样本集、非线性关系以及高维度特征识别问题,其有效性源于通过核函数将原始数据映射至更高维的空间,使得原本复杂的分类问题变得线性可分。 粒子群优化算法是种模拟鸟群社会行为的群体智能优化技术。在该算法框架下,每个潜在解被视作个“粒子”,粒子群在解空间中协同搜索,通过不断迭代更新自身速度与位置,并参考个体历史最优解群体全局最优解的信息,逐步逼近问题的最优解。在本应用中,PSO被专门用于搜寻SVM中影响模型性能的两个关键参数——正则化参数C与核函数参数γ的最优组合。 项目所提供的实现代码涵盖了从数据加载、预处理(如标准化处理)、基础SVM模型构建到PSO优化流程的完整步骤。优化过程会针对不同的核函数(例如线性核、多项式核及径向基函数核等)进行参数寻优,并系统评估优化前后模型性能的差异。性能对比通常基于准确率、精确率、召回率及F1分数等多项分类指标展开,从而定量验证PSO算法在提升SVM模型分类能力方面的实际效果。 本研究通过个具体的MATLAB实现案例,旨在演示如何将全局优化算法与机器学习模型相结合,以解决模型参数选择这关键问题。通过此实践,研究者不仅能够深入理解SVM的工作原理,还能掌握利用智能优化技术提升模型泛化性能的有效方法,这对于机器学习在实际问题中的应用具有重要的参考价值。 资源来源于网络分享,仅用于学习交流使用,请勿用于商业,如有侵权请联系我删除
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值