C++ 中 i++ 與 ++i 的本質區別
阿新 • • 發佈:2019-02-07
在某些場合部分地模擬指標的行為進行特殊資料操作的工具。Iter<_T>的程式碼如下:
說明:
Iter<_T>::Iter(_T *) :初始化構造器接受一個指標作為初始化引數;
Iter<_T>::Iter(const Iter &):複製構造器,裡面輸出一個 copy 字樣用來指示呼叫了複製構造器;
Iter & Iter<_T>::operator = (const Iter &)
Iter & Iter<_T>::operator = (_T *) :兩個賦值操作符,為了方便使用而引入;
Iter & Iter<_T>::operator ++ ():對應 ++i 的操作符,注意返回值為 Iter &;
Iter Iter<_T>::operator ++ (int):對應 i++ 的操作符,注意返回值為 Iter;
Iter<_T>::operator _T * () const:型別轉換操作符,使 Iter<_T> 具有代替 _T * 的能力。
使用Iter<_T>進行以下測試:
/*1*/之後輸出的內容是:++() *it: 2, *it2: 2
/*2*/之後輸出的內容是:++(int) copy() *it: 3, *it2: 3, *it3: 2
比較/*1*/和/*2*/容易看出 i++ 比 ++i 多呼叫了一個複製構造器。更仔細點分析容易知道,i++ 過程中先是分配了一個臨時實 例,然後把這個臨時例項複製出來,最後還要把這個臨時例項銷燬掉。這就是 ++i 與 i++ 的本質區別,也就是為什麼提倡優 先使用 ++i 的根本原因。
/*3*/之後輸出的內容是:++(int) copy() ++() *it: 4, *it2: 4, *it3: 2, *it4: 4
/*3*/的寫法是不提倡的,因為這個過程開始引入了極難察覺的複雜性。從中可以看出是先執行了 i++,而後再複製,再後就是 ++i 。這個過程難以從表示式 ++ i ++ 中獲得感性認識。按常人的理解,很容易解讀為先執行了 ++ i,而後執行了 i++。另一 個容易引入錯誤的是執行了這個表示式之後,it4 的內容開始變得模糊起來了,表面上看起來是對 it 的引用,但其實它具有嬗 變性,也就是說它並不總是 it 的引用,這會在接下來的過程中看到它的變化。這也就是為什麼要杜絕這類表示式的根本原因。
/*4*/之後輸出的內容是:++() copy() *it: 5, *it2: 5, *it3: 2, *it4: 5, *it5: 5
/*4*/與/*1*/所不同的是在/*1*/處沒有構造新的例項,it2是it的引用,而/*4*/則構造了新的例項,並呼叫了複製構造器。這裡的 另一個潛在的影響是對/*3*/的複合操作的,它潛在地改變了 it4 的引用,從此 it4 不再是 it 的引用,而是變成了 it5 的引用。
/*5*/之後輸出的內容是:++() *it: 5, *it2: 5, *it3: 2, *it4: 6, *it5: 6
/*5*/的作用是對 it5 進行 ++i 操作,更清楚地確認 it4 的確變成了 it5 的引用,而 it2 仍然是 it 的引用,/*3*/中的複合表示式的 不良影響在這裡最終體現出來了。
/*6*/之後輸出的內容是:++(int) copy() ++() copy() *it: 6, *it2: 6, *it3: 2, *it4: 6, *it5: 6, *it6: 6
/*6*/也是要杜絕的。但/*6*/與/*3*/相比更加安全些,因為在構造 it6 時使用了複製構造器,從此以後 it6 與 it 再無瓜葛。要杜絕 的原因與/*3*/是相當的,因為即使比/*3*/安全,但它所複製的內容仍然具有一定程度上的嬗變性,難以相信這條語句結束後 it6 的內容與 it 的內容是一致的。
/*7*/之後輸出的內容是:++() *it: 7, *it2: 7, *it3: 2, *it4: 6, *it5: 6, *it6: 6
/*8*/之後輸出的內容是:++(int) copy() *it: 8, *it2: 8, *it3: 2, *it4: 6, *it5: 6, *it6: 6
/*7*/與/*8*/形成對比,類似於/*1*/和/*2*/。但值得注意的是在/*8*/中即使沒有賦值操作,也需要構造一個臨時變數,並釋放這個 臨時變數。
/*9*/之後輸出的內容是:++(int) copy() ++() *it: 9, *it2: 9, *it3: 2, *it4: 6, *it5: 6, *it6: 6
/*9*/雖然也是要杜絕的,但比/*6*/相比安全性更好一些,因為這裡沒有賦值操作。一般情況下都能保證 it 被加了兩次。
/*10*/和/*11*/是在迴圈控制中使用 ++i 和 i++ 的對比,可以十分明顯地看到 ++i 用在迴圈控制操作中的效能改進優勢。
以上程式碼分析基於的編譯器是 GCC 3.4.5。
轉自:
http://hi.baidu.com/zighouse/blog/item/fd12fb0023907f1b7aec2ca6.html
template<typename _T> class Iter { public: _T* p; Iter(_T* __p) : p(__p) {} Iter(const Iter & __iter) : p(__iter.p) { cout << "copy() "; } Iter & operator = (const Iter & __iter) { p = __iter.p; return * this; } Iter & operator = (_T * __p) { p = __p; return * this; } Iter & operator ++ () { cout << "++() "; ++ p; return * this; } Iter operator ++ (int) { cout << "++(int) "; Iter result(*this); ++ p; return result; } operator _T * () const { return p; } };
說明:
Iter<_T>::Iter(_T *) :初始化構造器接受一個指標作為初始化引數;
Iter<_T>::Iter(const Iter &):複製構造器,裡面輸出一個 copy 字樣用來指示呼叫了複製構造器;
Iter & Iter<_T>::operator = (const Iter &)
Iter & Iter<_T>::operator = (_T *) :兩個賦值操作符,為了方便使用而引入;
Iter & Iter<_T>::operator ++ ():對應 ++i 的操作符,注意返回值為 Iter &;
Iter Iter<_T>::operator ++ (int):對應 i++ 的操作符,注意返回值為 Iter;
Iter<_T>::operator _T * () const:型別轉換操作符,使 Iter<_T> 具有代替 _T * 的能力。
使用Iter<_T>進行以下測試:
int main() { int array [] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 0}; Iter<int> it(array); cout << "*it: " << *it << endl; Iter<int>& it2 = ++ it; /*1*/ cout << "*it: " << *it << ", *it2: " << *it2 << endl; Iter<int> it3 = it ++; /*2*/ cout << "*it: " << *it << ", *it2: " << *it2 << ", *it3: " << *it3 << endl; Iter<int>& it4 = ++ it ++; /*3*/ cout << "*it: " << *it << ", *it2: " << *it2 << ", *it3: " << *it3 << ", *it4: " << *it4 << endl; Iter<int> it5 = ++ it; /*4*/ cout << "*it: " << *it << ", *it2: " << *it2 << ", *it3: " << *it3 << ", *it4: " << *it4 << ", *it5: " << *it5 << endl; ++ it5; /*5*/ cout << "*it: " << *it << ", *it2: " << *it2 << ", *it3: " << *it3 << ", *it4: " << *it4 << ", *it5: " << *it5 << endl; Iter<int> it6 = ++ it ++; /*6*/ cout << "*it: " << *it << ", *it2: " << *it2 << ", *it3: " << *it3 << ", *it4: " << *it4 << ", *it5: " << *it5 << ", *it6: " << *it6 << endl; ++ it; /*7*/ cout << "*it: " << *it << ", *it2: " << *it2 << ", *it3: " << *it3 << ", *it4: " << *it4 << ", *it5: " << *it5 << ", *it6: " << *it6 << endl; it ++; /*8*/ cout << "*it: " << *it << ", *it2: " << *it2 << ", *it3: " << *it3 << ", *it4: " << *it4 << ", *it5: " << *it5 << ", *it6: " << *it6 << endl; ++ it ++; /*9*/ cout << "*it: " << *it << ", *it2: " << *it2 << ", *it3: " << *it3 << ", *it4: " << *it4 << ", *it5: " << *it5 << ", *it6: " << *it6 << endl; for(Iter<int> i=array; i!=array+10; ++i) /*10*/ cout << endl; for(Iter<int> i=array; i!=array+10; i++) /*11*/ cout << endl; return 0; }
/*1*/之後輸出的內容是:++() *it: 2, *it2: 2
/*2*/之後輸出的內容是:++(int) copy() *it: 3, *it2: 3, *it3: 2
比較/*1*/和/*2*/容易看出 i++ 比 ++i 多呼叫了一個複製構造器。更仔細點分析容易知道,i++ 過程中先是分配了一個臨時實 例,然後把這個臨時例項複製出來,最後還要把這個臨時例項銷燬掉。這就是 ++i 與 i++ 的本質區別,也就是為什麼提倡優 先使用 ++i 的根本原因。
/*3*/之後輸出的內容是:++(int) copy() ++() *it: 4, *it2: 4, *it3: 2, *it4: 4
/*3*/的寫法是不提倡的,因為這個過程開始引入了極難察覺的複雜性。從中可以看出是先執行了 i++,而後再複製,再後就是 ++i 。這個過程難以從表示式 ++ i ++ 中獲得感性認識。按常人的理解,很容易解讀為先執行了 ++ i,而後執行了 i++。另一 個容易引入錯誤的是執行了這個表示式之後,it4 的內容開始變得模糊起來了,表面上看起來是對 it 的引用,但其實它具有嬗 變性,也就是說它並不總是 it 的引用,這會在接下來的過程中看到它的變化。這也就是為什麼要杜絕這類表示式的根本原因。
/*4*/之後輸出的內容是:++() copy() *it: 5, *it2: 5, *it3: 2, *it4: 5, *it5: 5
/*4*/與/*1*/所不同的是在/*1*/處沒有構造新的例項,it2是it的引用,而/*4*/則構造了新的例項,並呼叫了複製構造器。這裡的 另一個潛在的影響是對/*3*/的複合操作的,它潛在地改變了 it4 的引用,從此 it4 不再是 it 的引用,而是變成了 it5 的引用。
/*5*/之後輸出的內容是:++() *it: 5, *it2: 5, *it3: 2, *it4: 6, *it5: 6
/*5*/的作用是對 it5 進行 ++i 操作,更清楚地確認 it4 的確變成了 it5 的引用,而 it2 仍然是 it 的引用,/*3*/中的複合表示式的 不良影響在這裡最終體現出來了。
/*6*/之後輸出的內容是:++(int) copy() ++() copy() *it: 6, *it2: 6, *it3: 2, *it4: 6, *it5: 6, *it6: 6
/*6*/也是要杜絕的。但/*6*/與/*3*/相比更加安全些,因為在構造 it6 時使用了複製構造器,從此以後 it6 與 it 再無瓜葛。要杜絕 的原因與/*3*/是相當的,因為即使比/*3*/安全,但它所複製的內容仍然具有一定程度上的嬗變性,難以相信這條語句結束後 it6 的內容與 it 的內容是一致的。
/*7*/之後輸出的內容是:++() *it: 7, *it2: 7, *it3: 2, *it4: 6, *it5: 6, *it6: 6
/*8*/之後輸出的內容是:++(int) copy() *it: 8, *it2: 8, *it3: 2, *it4: 6, *it5: 6, *it6: 6
/*7*/與/*8*/形成對比,類似於/*1*/和/*2*/。但值得注意的是在/*8*/中即使沒有賦值操作,也需要構造一個臨時變數,並釋放這個 臨時變數。
/*9*/之後輸出的內容是:++(int) copy() ++() *it: 9, *it2: 9, *it3: 2, *it4: 6, *it5: 6, *it6: 6
/*9*/雖然也是要杜絕的,但比/*6*/相比安全性更好一些,因為這裡沒有賦值操作。一般情況下都能保證 it 被加了兩次。
/*10*/和/*11*/是在迴圈控制中使用 ++i 和 i++ 的對比,可以十分明顯地看到 ++i 用在迴圈控制操作中的效能改進優勢。
以上程式碼分析基於的編譯器是 GCC 3.4.5。
轉自:
http://hi.baidu.com/zighouse/blog/item/fd12fb0023907f1b7aec2ca6.html