c++野指针 之 实战篇

来源:互联网 发布:学编程在家里能赚钱吗 编辑:程序博客网 时间:2024/06/03 21:13

一:今天做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);//调用后置
这个是约定俗成



0 0
原创粉丝点击