1. 程式人生 > >C++ 中 i++ 與 ++i 的本質區別

C++ 中 i++ 與 ++i 的本質區別

在某些場合部分地模擬指標的行為進行特殊資料操作的工具。Iter<_T>的程式碼如下:
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