C++笔记121118
这里谈一下后自增操作符++
其实现在任何一个见过++i;和i++;的人都明白它的初步原理是啥。很多程序员都会说出下面这段话:
前自增代表先自增再操作,后自增代表先操作后自增。
这个先和后的说法实际上并不准确,例如下面这个程序:
1 #include<list> 2 3 int main() 4 { 5 list<int> testlist; 6 for(int ix=0;ix<10;++ix)testlist.push_back(ix); 7 8 list<int>::iter = testlist.begin(); 9 while(!testlist.empty()) 10 { 11 testlist.erase(iter++); 12 //wrong code: 13 //testlist.erase(iter);iter++; 14 } 15 return 0; 16 }
这段程序定义了一个装有int型数据的list容器,然后用0~9赋值。最后删除。
关键在于中间这一句怎么理解
testlist.erase(iter++);
如果按照“先操作再自增”的解释,那么似乎是先把iter所指的数据删除,然后iter指向下一个数据。但这是错误的。因为如果不用这一行,而改写成12~13行的样子,运行时会报错。原因就在于erase操作将导致指向list的迭代器iter失效。这时再写iter++就没有任何意义。
那么为什么testlist.erase(iter++)确是正确的呢?原因在于后自增操作符并不是简单的“先操作再自增”。实际上首先注意iter++是一个表达式,任何一个表达式都有一个返回值,而后自增表达式的返回值既不是iter+1也不是iter本身,而是尚未自增的iter的副本。换句话说,iter++这个表达式实际上做了三个操作:
1、构造一个iter的原来的值的副本。
2、iter自增1。
3、返回那个副本。
所以erase(iter++)操作的其实是iter原来值的副本,而且操作是在最后一步进行的。所以失效的是副本迭代器,而iter由于已经在第二步自增指向了下一个元素,所以没有问题。
而如果分开来写就不行了,因为erase(iter)后,iter已然失效,成为了悬垂的迭代器,这时再iter++是没有意义的。
虽然博主并不推崇谭浩强把i++,++i搞得错综复杂浪费时间。但是在这个问题中,出于安全性的考虑,这个细节还是重要的。当然,更常见的写法是:
iter = testlist.erase(iter);
这里用到erase()函数会返回一个指向被删除元素的下一元素的迭代器,并用它重新给iter赋值。但从可读性上来说,博主个人更加喜欢erase(iter++)的表达。
另外,从上述例子中可以看出来的是,iter++比++iter做的事情更多。对于简单的迭代器差别不大,但是对于复杂的迭代器类型,iter++是比++iter开销大的。从返回值上来说,iter++返回的是一个右值(副本),而++iter返回的是一个左值(自增后的iter本身)。如果没有特殊需要,仅仅想要自增的话,正如C++ Primer中所推荐的那样,最好通常只用前自增操作符。这就是为什么C++程序员在循环中都喜欢写++i而不是i++的原因。