一:今天做poj上的3750那个题,用到了list的erase方法。提交之后总是报runtime error!
纠结了好长时间。曾有一度怀疑过vector的erase和list的erase处理方式不一样。理论知识请參考也指针和悬浮指针:http://blog.csdn.net/u010700335/article/details/39831293 或 深拷贝和浅拷贝点击打开链接 http://blog.csdn.net/u010700335/article/details/39830425
二:现附上代码。稍后作解释
#include <iostream>
#include <cstdio>
#include <cstring>
#include <list>
using namespace std;
const int MAX_SIZE = 20;
int main()
{
int n,w,s;
int i,cp;
char str[MAX_SIZE];
list<string> lists;
scanf("%d",&n);
for(i=0;i<n;i++)
{
cin >> str;
lists.push_back(str);
}
scanf("%d,%d",&w,&s);
list<string>::iterator iter;
iter = lists.begin();
while(w>1)
{
iter++;
w--;
}
cp = 1;
for(; lists.size()!=0;)
{
cp++;
if(iter == lists.end())
iter = lists.begin();
if(cp<=s)
{
iter++;
}
else
{
cout << *iter << endl;
iter = lists.erase(iter);// 由于删除iter,自己主动返回下一个元素的,vector也是一样;否则ter成为野指针。而非自己主动指向下一个,是返回下一个。
cp = 1;
}
}
return 0;
}
三:原因例如以下:
程序里面使用了list容器,今天搞清楚了erase()函数的机理。经常使用的删除容器中元素的方法是例如以下(方法1):
list< int> List;
list< int>::iterator iter;
for( iter = List.begin(); iter != List.end(); )
{
if(1)
{
iter = List.erase( iter );
}
else
{
iter++;
}
}
也能够这样写(方法2):
list< int> List;
list< int>::iterator iter;
for( iter = List.begin(); iter != List.end(); )
{
if(1)
{
List.erase( iter++ );
}
else
{
iter++;
}
}
有一种错误的写法(注意同方法2比較)
list< int> List;
list< int>::iterator iter;
for( iter = List.begin(); iter != List.end(); )
{
if(1)
{
List.erase( iter );
}
iter++;
}//
我们看一下erase()函数的源码(仅列出release下的代码)。
iterator erase(iterator _Where)
{ // erase element at _Where
_Nodeptr _Pnode = (_Where++)._Mynode();
if (_Pnode != _Myhead)
{ // not list head, safe to erase
_Nextnode(_Prevnode(_Pnode)) = _Nextnode(_Pnode);
_Prevnode(_Nextnode(_Pnode)) = _Prevnode(_Pnode);
this->_Alnod.destroy(_Pnode);
this->_Alnod.deallocate(_Pnode, 1);
--_Mysize;
}
return (_Where);
}
函数在返回的时候,是返回当前迭代器的下一个节点。所以当 iter = List.erase( iter ); 运行以后。迭代器自己主动指向了下一个元素。
而对于入參中的iter。所指的地址已经被销毁,所以写的时候,应该注意加上前面的iter =
四:iter++ 和 ++iter
那另外的一种写法,List.erase( iter++ ); 为什么也是对的呢?这里研究了一下,这里须要讲一下++运算符的操作。
_Myt_iter& operator++() // 前面自增
{ // preincrement
++(*(_Mybase_iter *)this);
return (*this);
}
_Myt_iter operator++(int) // 后面自增
{ // postincrement
_Myt_iter _Tmp = *this;
++*this;
return (_Tmp);
}
++实际上能够看做是一个函数。
对于++在后的情况(比如i++),函数在执行的时候,将运算的数据i已经改变。可是函数的返回值是操作之前的数据。所以在我们看来,i++好像是先进行了i的读取。才+1。
回到迭代器,List.erase( iter++ );就没有问题了。
对于那种错误的方法,List.erase( iter );在运行以后,iter所指的对象已经被销毁,所以再对iter进行操作是非法的,程序会出错。
再看一个简单的演示样例:
#include <iostream>
using namespace std;
class A
{
public:
A(int a=0,int b=0)
{
x=a;
y=b;
}
A& operator++();
A operator++(int);
void out()
{cout<<x<<","<<y<<endl;
}
private:
int x,y;
};
A& A::operator++()
{
x++;
y++;
return *this;
}
A A::operator++(int)
{
return *this;
x++;
y++;
}
void main()
{
A ta(1,2),tb(1,2);
ta.out();
tb.out();
(++ta).out();
(tb++).out();
}
依据书上的形式写了个程序,A&前置和A型后置,程序正确,就是有个疑问:
为什么编译器会自己主动调用A&型。A型。它怎么知道在++号在ta前面时。要调用A&;++号在ta后面时,要调用A;到底是怎么办到的?
重载的++ 符号在使用过程中 编译器是不能区分出 ++是前置的还是 后置的?是依据(int)来区分 当给定实參(比方0)时为后置
opertor++();//重载前置++
operator++(int); //重载后置++
c.opertor++;//调用前置
c.opertor++(0);//调用后置
这个是约定俗成