C++ | C++入門總結(三)迭代器、數組

  • 迭代器

  1. 所有標準庫容器都可以使用迭代器,比如vector,嚴格來講,string對象不屬於容器類型,但是string支持很多與容器類型類似的操作,string支持迭代器。
  2. 使用迭代器可以訪問某個元素,迭代器也能從一個元素移動到另外一個元素。
  3. 可以使用迭代器的這些類型中,都有begin和end成員,這兩個成員可以返回迭代器:begin負責返回指向第一個元素(或第一個字符)的迭代器;end負責返回指向容器(或string對象)“尾元素的下一位置”的迭代器,也就是說,end返回的這個迭代器指向的是容器的一個本不存在的“尾後”元素,這樣的迭代器沒有什麼實際含義,僅是一個標記而已,表示我們已經處理完了容器中的所有元素。end 成員返回的迭代器常被稱作尾後迭代器或者簡稱爲尾迭代器。
  4. 特殊情況下如果容器爲空,則 begin 和 end 返回的是同一個迭代器,都是尾後迭代器。
  5. 和指針類似,也能通過解引用迭代器來獲取它所指示的元素,執行解引用的迭代器必須合法並確實指示着某個元素。 試圖解引用一個非法迭代器或者尾後迭代器都是未被定義的行爲。
  6. 檢查一個字符串是否爲空,可以通過檢查 begin 和 end 返回的結果是否一致,如果結果一樣,說明字符串爲空。
  7. 箭頭運算符(->):箭頭運算符把解引用和成員訪問兩個操作結合在一起,解引用運算符的優先級低於點運算符,(*it).empty()可寫成it->empty()。

*1.因爲 end 返回的迭代器並不實際指示某個元素,所以不能對其進行遞增或解引用的操作。

*2.一般來說我們也不知道(其實是無須知道)迭代器的精確類型。而實際上,那些擁有迭代器的標準庫類型使用 iterator 和 const_iterator來表示迭代器的類型。(const_iterator:能讀取但不能修改)

*3.如果 vector 對象或 string 對象是一個常量,begin和end返回const_iterator;

*4.如果 vector 對象或 string 對象不是常量,begin和end返回iterator。

*5.如果對象只需讀操作而無須寫操作的話最好使用常量類型 ( 比如const_iterator)。爲了便於專門得到const_iterator類型的返回值 , C ++11 新標準引入了兩個新函數 , 分別是 cbegin和 cend。

*6.不能在範圍 for 循環中向 vector 對象添加元素。但凡是使用了迭代器的循環體,都不要向迭代器所屬的容器添加元素。(在範圍 for 語句中 , 預存了 end ( ) 的值 . 一旦在序列中添加 ( 刪除 ) 元素 , end 函數的值就可能變得無效了)

#include <iostream>
#include <string>
#include <cctype>
using namespace std;

int main()
{
	string str("hello world");
	if (str.begin() != str.end())
	{
		auto it = str.begin();
		if (islower(*it)) 
		{
			*it = toupper(*it);
		}
	}
	cout << str << endl;
	return 0;
}
	vector<string> sVec(2, "hello world");
	for (vector<string>::const_iterator it = sVec.cbegin(); it!= sVec.cend() && !it->empty(); ++it)
	{
		cout << *it << endl;
	}
  • 迭代器運算符: 

  • string和vector提供了更多額外的運算符:
  1. 向前、向後移動多個元素:iter+n、iter-n;
  2. 兩個迭代器相減,獲取距離:距離指的是右側的迭代器(減數,減號右邊的迭代器)向前移動多少位置就能追上左側的迭代器(被減數,減號左邊的迭代器),其類型是名爲 difference_type 的帶符號整型數。string 和vector都定義了difference_type。因爲這個距離可正可負,所以difference _ type 是帶符號類型的。

	//尋找字符串中是否有2
	string text("012345678");
	auto begin = text.begin();
	auto end = text.end();
	auto mid = begin + (end - begin) / 2;
	cout << *mid << endl;
	while (mid != end && *mid != '2')
	{
		
		if (*mid > '2')
		{
			end = mid;
		}
		else {
			begin = mid + 1;
		}
		mid = begin + (end - begin) / 2;
		cout << *mid << endl;
	}
	if (mid == end)
	{
		cout << "不存在要找的數";
	}
	else {
		cout << "存在要找的數";
	}

  • 數組

  1. 與vector相似,數組也是存放類型相同的對象的容器(所以不存在引用的數組),但與vector不同的是,數組的大小確定不變,不能隨意向數組中增加元素。
  2. 定義數組時,必須指定數組類型,不允許用auto關鍵字由初始值的列表推斷。
  3. C++聲明數組時,格式爲a[d]:a表示數組的名字,d表示數組元素的個數(必須大於0),d必須是一個常量表達式。
  4. 對數組元素進行列表初始化時,可以忽略數組維度,編譯器會根據初始值的數量計算並推斷出來;不忽略數組維度時,維度應大於等於元素個數。
  5. 字符數組還有一種初始化的形式,使用字符串字面值進行初始化。使用這種方式時,應該注意字符串字面值的結尾處還有一個空字符,這個空字符也會像字符串的其他字符一樣被拷貝到字符數組中去。
  6. 在使用數組下標的時候,通常將其定義爲 size_t 類型。 size_t是一種機器相關的無符號類型,它被設計得足夠大以便能表示內存中任意對象的大小。

C#:

            int numLength = 10;
            int[] nums=new int[numLength];

C++:

	constexpr int numLength = 10;
	int nums[numLength];

C++數組初始化: 

	int nums1[5] = { 0,1,2,3 };//5個元素{0,1,2,3,0}
	int nums2[] = { 0,1,2,3 };//4個元素
	//int nums3[2] = { 0,1,2 };//錯誤,維度小於元素個數

	string strs1[3] = { "hello","world" };//3個元素{ "hello","world","" }

	char chars[] = "hello";//6個元素
	int length= sizeof(chars) / sizeof(chars[0]);
	cout << length << endl;//6

  • 指針和數組

  1. 對數組的元素使用取地址符(&)就能得到指向該元素的指針;
  2. 在很多用到數組名字的地方,編譯器都會自動地將其替換爲一個指向數組首元素的指針。
  3. 當定義一個auto變量,將數組作爲其初始值時,該變量類型是指針而不是數組。
  4. 當使用decltype關鍵字獲取數組的類型時,獲取到的類型和數組的原類型相同。
	string nums[] = { "one","two","three" };

	string *p = &nums[0];
	string *p = nums;//同上

	auto nums1(nums);//nums1是一個字符串類型的指針,指向nums的第一個元素
	auto nums1(&nums[0]);//同上

	decltype(nums) nums2;//nums2是一個含有三個字符串元素的數組,即 string nums2[3];
  1.  指針也是迭代器,使用指針也可以遍歷數組中的元素;
  2. 獲取指向數組第一個元素的指針:&nums[0]或者使用begin(nums)函數;
  3. 獲取指向數組尾元素的下一位置的指針:&nums[nums的長度]或者使用end(nums)函數;尾後指針不指向具體的元素,所以,不能對尾後指針執行解引用或者遞增的操作。
  4. 因爲數組不是類類型,和vector等容器不同,所以,begin和end不是成員函數,正確的使用形式是將數組作爲它們的參數。這兩個函數定義在iterator頭文件中。
  5. 和迭代器一樣 , 兩個指針相減的結果是它們之間的距離。參與運算的兩個指針必須指向同一個數組當中的元素 。
  6. 兩個指針相減的結果的類型是一種名爲 ptrdiff_t 的標準庫類型,和 size_t 一樣,ptrdiff_t 也是一種定義在 cstddef 頭文件中的機器相關的類型。因爲差值可能爲負值,所以ptrdiff_t 是一種帶符號類型。(在vcruntime.h文件中,發現ptrdiff_t 就是int類型)
#include <string>
#include <iterator>
using namespace std;
int main()
{
	int nums[] = {0,1,2,3,4,5};
	auto *beginNums = begin(nums);//beginNums是int類型的指針,指向nums的第一個元素
	int *endNums = end(nums);
	int *midNums = nums + 2;//midNums指向nums的第二個元素
	ptrdiff_t dis1 = endNums - midNums;
	cout << dis1 << endl;//4

	string strs[3] = { "hello","world" };
	auto *beginStrs = begin(strs);//beginStrs是string類型的指針,指向strs的第一個元素
	string *endStrs = end(strs);
	int dis2 = endStrs - beginStrs;
	cout << dis2 << endl;//3
}

*1.內置的下標運算符所用的索引值不是無符號類型,標準庫類型(vector)與string限定使用的下標必須是無符號類型。

*2.對數組執行下標運算其實是對指向數組元素的指針執行下標運算。

	int nums[] = { 0,1,2,3 };
	int *p = &nums[2];
	int a = p[1];//p[1]想當於*(p+1),即nums[3]
	int b = p[-2];//p[1]想當於*(p-2),即nums[0]
  • 數組和vector

  1. 不允許使用一個數組爲另一個內置類型的數組賦初值 , 也不允許使用 vector 對象初始化數組。
  2. 允許使用數組來初始化 vector 對象,要實現這一目的 , 只需指明要拷貝區域的首元素地址和尾後地址就可以了。
	int nums[] = { 0,1,2,3 };

	int nums1 = nums;//錯誤

	vector<int> numVec{ 0,1,2,3 };
	int nums2 = numVec;//錯誤

	//vector<int> numsVec = { begin(nums), end(nums) };
	//vector<int> numsVec{ begin(nums), end(nums) };
	vector<int> numsVec(begin(nums), end(nums));//正確
	cout << numsVec.size() << endl;//4
	for (auto a : numsVec)
	{
		cout << a << endl;
	}

  • 多維數組

  1. 嚴格來說, C ++ 語言中沒有多維數組,通常所說的多維數組其實是數組的數組。
  2. 當一個數組的元素仍然是數組時,通常使用兩個維度來定義它:一個維度表示數組本身大小,另外一個維度表示其元素(也是數組)大小。
  3. 對於二維數組來說,常把第一個維度稱作行(row),第二個維度稱作列(col)。
  4. 多維數組初始化:a.允許使用花括號括起來的一組值初始化多維數組;b.類似於一維數組,在初始化多維數組時也並非所有元素的值都必須包含在初始化列表之內,其他未列出的元素執行默認值初始化。
  5. 如果表達式含有的下標運算符數量和其數組的維度一樣多,該表達式的結果將是給定類型的元素;反之,如果表達式含有的下標運算符數量比其數組的維度小,則表達式的結果將是給定索引處的一個內層數組。
  6. 使用範圍 for 語句只讀多維數組時,除了最內層的循環外,其他所有循環的控制變量都應該是引用類型(避免數組被自動轉成指針);要修改多維數組時,都應該是引用類型。
	int array0[3][4] = { {0,1,2,3},{4,5,6,7},{8,9,10,11} };
	int array1[3][4] = { 0,1,2,3,4,5,6,7,8,9,10,11 };
	int array2[3][4] = { {0},{1} };//{0,0,0,0,1,0,0,0,0,0,0,0}
	int array3[3][4] = { 0, 1 }; //{0,1,0,0,0,0,0,0,0,0,0,0}
	int a[3][4];
	int b[2][4] = { 0,1,2,3,4,5,6,7 };
	a[2][3] = b[1][1];// b[1][1]下標運算符數量爲2=數組b維度2
	int (&c)[4] = b[1];// b[1]下標運算符數量爲1 != 數組b維度2
	int b[2][4] = { 0,1,2,3,4,5,6,7 };
	int offset = 1;
	for (auto &row : b) {
		for (auto &col : row)
		{
			col += offset;
		}
	}

	for (auto &row : b) {
		for (auto col : row)
		{
			cout << col << endl;
		}
	}
  • 指針和多維數組
  1. 當程序使用多維數組的名字時,也會自動將其轉換成指向數組首元素的指針。
  2. 通過使用 autc 或者 decltype就能儘可能地避免在數組前面加上一個指針類型了。
	int a[3][4];
	int *p1[4]; //相當於int* p1[4];p1是存放int指針類型的數組
	int (*p2)[4] = a;//p2是指針,指向int類型的數組;p2指向a的首元素,該首元素是一個數組,含有4個整型數
	p2 = &a[2];//p2指向a的尾元素

	auto p3 = a;//相當於	int (*p3)[4] = a;

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章