Container Traverse
node
for(iterator it = begin(); it != end(); ++it)程序員
for(iterator it = begin(); it != end(); it++)
兩種方式iterator遍歷的次數是相同的,但在STL中效率不一樣,前++--返回引用,後++--返回一個臨時對象,由於iterator是類模板,使用it++這種形式要返回一個無用的臨時對象,而it++是函數重載,因此編譯器沒法對其進行優化,因此每遍歷一個元素,你就建立並銷燬了一個無用的臨時對象。
不信的話你能夠去看看C++的標準庫,還有符合標準C++的教材,除了特殊須要和對內置類型外,基本都是使用++it來進行元素遍歷的,無論是源代碼仍是教材中都是如此。
用戶定義類型對操做符的重載應與內置操做符的行爲類似,並且後自增/減每每是引用前自增/減來做爲其實行的一個副本。
好比一般都是這種形式:
class foo
{
public:
foo& operator ++ (){return ++bar;}
foo operator ++ (int)
{
foo tmp = *this; // 建立臨時對象 ★
++*this; // 調用前自增
return tmp; // 返回臨時對象 ★
}
private:
int bar;
}
以上標★號的2個步驟有時是多餘的,好比用STL中用iterator遍歷容器,這樣就形成了沒必要要的程序效率的損失。
這也是被一些從C移植到C++的程序員所頻頻忽視的細節,因此它們被稱爲從C帶到C++中的編程惡習。
More Effective C++
Item 6: Distinguish between prefix and postfix forms of increment and decrement operators.
對C++中的前/後自增/減操做符以及因C++的重載對他們所引起的效率問題有詳細的講解。如下是一部份內容:
If you're the kind who worries about efficiency, you probably broke into a sweat when you first saw the postfix increment function. That function has to create a temporary object for its return value (see Item 19), and the implementation above also creates an explicit temporary object (oldValue) that has to be constructed and destructed. The prefix increment function has no such temporaries. This leads to the possibly startling conclusion that, for efficiency reasons alone, clients of UPInt should prefer prefix increment to postfix increment unless they really need the behavior of postfix increment. Let us be explicit about this.
When dealing with user-defined types, prefix increment should be used whenever possible, because it's inherently more efficient. (注意這一句)
Let us make one more observation about the prefix and postfix increment operators. Except for their return values, they do the same thing: they increment a value. That is, they're supposed to do the same thing. How can you be sure the behavior of postfix increment is consistent with that of prefix increment? What guarantee do you have that their implementations won't diverge over time, possibly as a result of different programmers maintaining and enhancing them? Unless you've followed the design principle embodied by the code above, you have no such guarantee. That principle is that postfix increment and decrement should be implemented in terms of their prefix counterparts. You then need only maintain the prefix versions, because the postfix versions will automatically behave in a consistent fashion.
C++ Erase List Node Eror , list iterator not incrementable
剖析STL容器遍歷刪除時詭異的erase(iter++)
STL中結點類容器(如:list,hash_map)遍歷時進行刪除時,須要這樣作:
for(list <int> ::iterator iter = m_list.begin(); iter != m_list.end(); )
{
if(須要刪除)
{
m_list.erase(iter++);
}
else
++iter;
}
而不能這樣:
for(list <int> ::iterator iter = m_list.begin(); iter != m_list.end(); ++iter)
{
if(須要刪除)
{
m_list.erase(iter);
}
}
爲何呢?
以STL list爲例:
iterator的相關操做
_Self& operator++()
編程
{
this-> _M_incr();
return *this;
}
_Self operator++(int)
{ _Self __tmp = *this;
this-> _M_incr();
return __tmp; //後綴++按照語意返回了++前的iterator,
}
void _M_incr() { _M_node = _M_node-> _M_next; } //++的操做對於list結構來講,就是使iterator的_M_node指向下一個結點
iterator erase(iterator __position)
{ _List_node_base* __next_node = __position._M_node-> _M_next;
_List_node_base* __prev_node = __position._M_node-> _M_prev;
_Node* __n = (_Node*) __position._M_node;
__prev_node-> _M_next = __next_node;
__next_node-> _M_prev = __prev_node; //上面的代碼把刪除結點__position的先後結點串起來,而移除_positoin
_STLP_STD::_Destroy(&__n-> _M_data); //call T::~T()
this-> _M_node.deallocate(__n, 1); //釋放結點內存
return iterator((_Node*)__next_node);
}
分析代碼咱們能夠看出,erase會deallocate__position的_M_node, 在__position上再進行++是錯誤的。
因此不能在m_list.erase(iter)後,進行iter++.
哪爲何m_list.erase(iter++)能夠呢?爲何不能用m_list.erase(++iter)?
參照operator++的代碼咱們能夠找到答案,iter++返回了++以前的iter值,erase使用這個值能正確進行__position的先後結點的串接及刪除正確的結點,而++iter返回的是++以後的iter,因此m_list.erase(++iter)串接不正確,iter-> _M_node也是失效的.
對於非結點類,如數組類的容器vector,string,deque,若是erase會返回下個有效的iterator,能夠這樣處理:
for(vector <int> ::iterator iter = m_vector.begin(); iter != m_vector.end();)
{
if(須要刪除)
{
iter=m_vector.erase(iter);
}
else
++iter;
}
數組