const 迭代器和const 迭代型

2021-06-29 11:05:41 字數 3810 閱讀 6692

正如大多c++程式設計人員所知的,每個標準容器類都提供四種迭代器型別。對於container而言,iterator的作用相當於t*,而const_iterator則相當於const t*(與t const* 意思一樣)。增加乙個iterator或者const_iterator可以在乙個從容器開頭向尾部的遍歷中讓你移動到容器的下乙個元素。reverse_iterator與const_reverse_iterator同樣相當於對應的t*和const t*,所不同的是,增加reverse_iterator或者const_reverse_iterator會在從尾到頭的遍歷中讓你移動到容器的下乙個元素。

首先讓我演示兩個東西。第一,看看vector的insert和erase的樣式:

iterator insert(iterator position, const t& x);

iterator erase(iterator position);

iterator erase(iterator rangebegin, iterator rangeend);

每個標準容器都包含了和這差不多的函式,雖然返回型別因容器型別的不同而不同。需要注意的是:這些方法只接受iterator型別的引數,而不是const_iterator、reverse_iterator或const_reverse_iterator。雖然容器類支援四種迭代器型別,但其中的一種型別有著其他所沒有的特權,那就是iterator。

我要讓你看的第二個東西是這張圖,它顯示了幾種迭代器之間存在的轉換關係:

圖中顯示了從iterator到const_iterator、從iterator到reverse_iterator和從reverse_iterator到const_reverse_iterator可以進行隱式轉換。並且,reverse_iterator可以通過呼叫其base成員函式轉換為iterator。const_reverse_iterator也可以類似地通過base轉換成為const_iterator。乙個圖中無法顯示的事實是:通過base得到的也許並非你所期待的iterator。

讓我們再進一步了解iterator、const_iterator、const iterator三者之間的區別,通過下面的**段來說明:

vectorivec;

vector::iterator iter0 = ivec.begin();

vector::const_iterator iter1 = ivec.begin(); //常量迭代器

const vector::iterator iter2 = ivec.begin();  //常量迭代型

*iter0 = 1;  //right

*iter1 = 1;  //error 不能用const_iterator改變其中元素

*iter2 = 1;  //right

++iter0;     //right

++iter1;     //right 改變迭代器,而不是其中的元素

++iter2;     //error

//總而言之:常量迭代器不同於常量迭代型。這意味著你能夠改變常量迭代器指向的位置(比如做自加++)。然而,你不能改變常量迭代器所指向的物件的值。

但是,千萬不要傻乎乎的宣稱const迭代器一無是處。不,它們可以與演算法默契配合,因為演算法通常並不關心迭代器是什麼型別,只要是適當的種類就可以了,很多容器的成員方法也接受const迭代器。只有insert和erase的一些形式有些吹毛求疵。

typedef dequeintdeque;     

typedef intdeque::iterator iter;

typedef intdeque::const_iterator constiter;

constiter ci;          // ci是const_iterator

...iter i(ci);                      // 錯誤!沒有從const_iterator到iterator隱式轉換的途徑

iter i(const_cast(ci));    // 仍是個錯誤!不能從const_iterator對映為iterator!

事實表明,把const迭代器對映為迭代器是病態的。

我們並不會就這樣束手無策,有一種安全的、可移植的方法獲取它所對應的iterator[,而且,用不著陷入型別系統的轉換。下面是解決思路的本質,雖然在它編譯前還要稍作修改:

typedef dequeintdeque;         // 和以前一樣

typedef intdeque::iterator iter;

typedef intdeque::const_iterator constiter;

intdeque  d;

constiter ci;

...  // 讓ci指向d

iter i(d.begin());          // 初始化i為d.begin()

// 把i移到指向ci位置(但請留意下面關於為什麼在它編譯前要調整的原因)

advance(i, distance(i, ci));

這種方法看上去非常簡單,直截了當,也很讓人吃驚吧。要得到與const_iterator指向同一位置的iterator,首先將iterator指向容器的起始位置,然後把它向前移到和const_iterator距離容器起始位置的偏移量一樣的位置即可!這個任務得到了兩個函式模板advance和distance的幫助,它們都在中宣告。distance返回兩個指向同乙個容器的iterator之間的距離;advance則用於將乙個iterator移動指定的距離。如果i和ci指向同乙個容器,那麼表示式advance(i, distance(i, ci))會將i移動到與ci相同的位置上。

如果這段**能夠通過編譯,它就能完成這種轉換任務。但似乎事情並不那麼順利。想知道為什麼,先來看看distance的定義:

template

typename iterator_traits::difference_type

distance(inputiterator first, inputiterator last);

不要被這個函式的長達個字元的返回型別卡住,也不用理會difference_type是什麼東西。取而代之的是,把注意力集中在引數的型別inputiterator:

當遇到distance呼叫時,你的編譯器需要根據使用的實參型別推斷出inputiterator的型別。再來看看我所說的不太正確的distance呼叫(如下):

advance(i, distance(i, ci));              // 調整i,指向ci位置

上面的呼叫有兩個引數傳遞給distance,i和ci。i的型別是iter,即deque::iterator的typedef。對編譯器來說,這表明呼叫distance的inputiterator是deque::iterator。但ci是constiter,即deque::const_iterator的typedef。這表明那個inputiterator是deque::const_iterator。inputiterator不可能同時有兩種不同的型別,所以呼叫distance失敗。一般會造成一些冗長的出錯資訊,可能會也可能不會說明是編譯器無法得出inputiterator是什麼型別。

要順利地呼叫distance,你需要排除歧義。最簡單的辦法就是顯式的指明distance呼叫的模板引數型別,從而避免編譯器自己得出它們的型別:

advance(i, distance(i, ci));

我們現在知道了怎麼通過advance和distance獲取const_iterator相應的iterator了。但另乙個我們現在一直避開卻很值的考慮的實際問題是:這個技巧的效率如何?答案很簡單。取決於你所轉換的究竟是什麼樣的迭代器。對於隨機訪問的迭代器(比如vector、string和deque的)而言,這是常數時間的操作。對於雙向迭代器(也就是,所有其它容器和包括雜湊容器的一些實現)而言,這是線性時間的操作。

const物件和const函式

當乙個類的函式的參數列不同時,能過載函式,當函式的const性不同時,也能過載函式。如下所示 include a.h 定義了ca類 class cbigclass const ca getvalue const ca getvalue private ca m value 客戶端 如下 ca ca ...

頂層const和底層const

頂層const 本身是乙個常量 底層const 所指的物件是乙個常量 int const p1 i const修飾p1,p1本身是乙個const,所以這個const是頂層const const int ci 42 const修飾ci,ci本身是乙個const,頂層 const int p2 ci c...

頂層const和底層const

1.頂層 const 與底層 const概念 指標本身是乙個物件,因為,指標實際對應著記憶體單元的一段儲存空間,然而,指標所指向的也是乙個資料物件,因此,指標是乙個常量與指標所指向的是乙個常量是兩個完全不同的概念,頂層 const表示的是指標本身是乙個常量,底層const 表示的是指標所指的物件是乙...