正文之前
今天幫學妹選了一天的電腦配件,然后從中領悟:坐看狗東黑我錢,任他漲價我不動!!最后果斷的用學妹的錢沖了個Plus,然后領了一堆券,最后學妹還省了30塊,另外昨晚找到了一個買酷睿i7 7700k 送主板的,又省了250,然后各種大小活動都去看了個遍,總算是學會了怎么省錢,特此還要多謝我華科電信的一位大佬--“大王”巍的指點,不然我肯定被狗東坑到懷疑人生。看配置的直接翻到正文之后看!
正文
1、 命名空間using聲明
我們自己平時用的時候是:
using namespace std;
老實說我也就會這一句,所以其他的基本漠不關心,但是既然書里講了我也就寫出來了。一般的格式如下:
using std::cin;
有了上面這句,以后寫cin就只是cin了。不需要std::cin了。不過也僅限于此,cout還是沒法直接用,不過這樣的好處就是可以單獨的引用,不會造成浪費(Maybe)?
另外頭文件不應該包含著using聲明,因為頭文件的內容會被拷貝到所有引用他的文件中取去,如果頭文件內有這個using聲明,那么每一個使用了該文件的文件就會有這個聲明,對于某些程序來說,由于不經意間包含了一些名字,反而可能會產生始料未及的名字沖突。
2、 標準庫 string的基本操作
首先,要使用string這個標準庫就要聲明下頭文件
#include <string>
using std::string
- string的初始化
有以下幾種種方式
string s1; //s1:empty
string s2=s1; //s2: empty
string s3="string"; //s3:string\0
string s4(10,'c') //s4:cccccccccc
string s5("string"); //s5:string\0
幾種方式的過程和效果我都寫出來了,就不多贅述了。
- string對象的上的操作
操作形式 | 效果 |
---|---|
cout<<s | 標準輸出流。輸出整個字符串 |
cin>>s | 從cin標準輸入流中一次讀取,直到遇到空白符號停下 |
getline(cin,s) | 從cin標準輸入流中一次讀取一行,不到換行符不停止讀取,可以讀空白 |
s.empty | 判斷是否為空,為空則返回true(bool) |
s.size() | 直接讀取字符串的字符個數,包括’\0‘ |
s[n] | 直接讀取s這個字符串中第n個字符,從0開始計算 |
s1+s2 | 連接兩個字符串 |
s1=s2 | 拷貝賦值 |
s1==s2 | 判斷兩個字符串是否相等,相等則返回true(bool) |
s1!=s2 | 判斷兩個字符串是否不等,不等則返回true(bool) |
<,>,<=,>= | 關系運算符,進行字符串的大小比較,后面說 |
- 下面詳細說明各個操作:
讀寫操作,讀取的時候自動忽略開頭的空白,遇到第一個非空字符開始讀取,之后遇到第一個空白字符就停止讀取,不管后面還有沒有,也不讀取空白字符,輸入“ hello world !!”,最后在cin>>s 中讀取到的只有s=“hello\0”;
讀取操作可以多個對象一起進行:cin>>s1>>s2; 那么“ hello world !!”最后的結果是:s1=“hello”;s2=“world”;
使用getline(cin,s)讀取一整行,getline()是無返回值函數,直接對s進行操作,所以不需要額外的空間承載返回值;getline()見到"\0"就結束輸入,但是得到的字符串不含有換行符,所以如果要按行輸出,那么就要自己手動加上換行操作。
empty() ,size()操作都是很顯而易見的,帶有返回值的函數,而且不需要傳入參數,是string自帶的一個成員函數,只需要使用點操作符調用即可:s.empty() s.size() ,前者返回一個bool變量,后者直接返回一個string::type_size的無符號整數變量,它能夠存放下任何string的大小,是其專屬的大小變量。若要在外部定義string::type_size類型的變量,可以用auto或者是decltype得到。
auto len=s.size(); decltype(s.size()) len;
比較運算符其實也沒啥差別,但是比較大小的話,string中有一套規則(但是我自己總結下來:只要從頭開始看起,第一個不相同的對應位置的字符比較大小就全權代表了這個字符串的大小,另外結束符號小于一切的字符!通用,不信你看):
* 如果兩個string長度不同,較短的string對象每個字符都與較長的對象對應部分相同,那么較短的就短于長的;(like:abc < abcd )
* 如果在相應的位置不一樣,那么就比較第一個相異的字符;(like: abd > abcd )相加相減操作:就直接加上咯,相當于拼接,反正string是動態增長長度的,所以隨便你加多少。還有一種騷操作,那就是直接用原本的字符串變量直接加上一個字符串:但是記住,加號兩邊一定要有一個是string變量,兩個字符串字面值直接加起來是違法操作!``` string s2=s1+"zhang"+"z"+"b" //從左到右相加,所以左邊一直都是string變量!!
3、 處理string對象中的字符
字符操作形式 | 效果(s指string對象中的單個字符) |
---|---|
isalnum(s) | 判斷字符是否是字母或數字; |
isalpha(s) | 判斷字符是否是字母; |
iscntrl(s) | 判斷字符是否是控制字符; |
isdigit(s) | 判斷字符是否是數字; |
isgraph(s) | 判斷字符是否是可打印的非空格字符; |
ispunct(s) | 判斷字符是否是標點符號; |
isspace(s) | 判斷字符是否是空白字符; |
isupper(s) | 判斷字符是否是大寫字母; |
isxdigit(s) | 判斷字符是否是十六進制數; |
toupper(s) | 轉換為大寫字母; |
tolower(s) | 轉換為小寫字母。 |
采用基于范圍的for循環實現:
for ( auto c:str) //此句的意思是:對于str中的每個字符進行拷貝
cout<<c<<endl;
上述只能實現讀而不能實現存,因為是直接拷貝,所以無法對string的單個字符進行操作,但是如果是引用,就可以實現了。
for ( auto &c:str) //此句的意思是:對于str中的每個字符進行引用
{
cout<<c<<endl;
c=toupper(c);
}
利用這些特性,結合一個大家常見的下標運算符(這個點就不講了,跟數組一樣,爛大街了),我自己實現了一個把所有的英文單詞的首字母改成大寫的程序:
string w="hello boy I don't want to hurt you? baby~~~";
w[0]=toupper(w[0]);
for (decltype(w.size()) index=0; index < w.size(); ++index)
{
if (isspace(w[index]))
{
w[index+1]=toupper(w[index+1]);
}
}
cout<<"================================================\n"<<w<<"\n================================================\n"<<endl;
4、 標準庫類型Vector定義與初始化
- vector是一種對象的集合,可以看作是一種容易,好比是房子,前面的string可以看做是教室排座位,每個座位上按序號只能坐入字符,不能是別的類型,并且多個字符可以組成一個小組,string對象就是一個小組;而vector就好比是大樓,可以安置的類型更加寬廣。可以放入int,string,char等一些類型。因為在具體定義一個vector對象前不知道類型,所以我們稱之為類模板,對其實施創建的時候稱此過程為實例化,定義如下:
vector<int> ivec;
vector<string> strvec;
vector<vector<int> > vecivec;
因為vector是容納對象的,所以不存在包含引用的vector;初始化如下:
vector<T> tvec; // Empty vector
vector<T> tvec(v1); // copy v1 to tvec
vector<T> tvec = v1 ; // 同上
vector<T> tvec(n,val); //tvec包含了n個相同T類型的val元素;
vector<T> tvec(n); //執行n次空的初始化;
vector<T> tvec{a,b,c,d ···}; //具體的初始化
vector<T> tvec={a,b,c,d ···}; //同上
注意如果是拷貝初始化的話,不同類型的vector是不能相互拷貝的!
5、 向vector中添加元素
對于vector這個容器,只能用專用的內置函數來對其增加元素,push_back具體用法如下:
vector<int> ivec;
for(int i=0;i!=100;++i)
{
ivec.push_back(i);
}
從上面我們可以知道,vector具有良好的動態增長的性能,所以一開始如果就限定其大小的話,是一種對特性的浪費,一開始初始化的時候就限定容量是不是一件明智的事情!(注意,其實上面的操作不是很符合規定,因為隱性規定:在循環體中改變了遍歷序列的長度的操作不用for 循環,可能造成緩沖區溢出)
6、 其他vector操作
匯總的Vector操作,假設c是vector變量 | 效果 |
---|---|
c.clear() | 移除容器中所有數據。 |
c.empty() | 判斷容器是否為空。 |
c.erase(pos) | 刪除pos位置的數據 |
c.erase(beg,end) | 刪除[beg,end)區間的數據 |
c.front() | 傳回第一個數據。 |
c.insert(pos,elem) | 在pos位置插入一個elem拷貝 |
c.pop_back() | 刪除最后一個數據。 |
c.push_back(elem) | 在尾部加入一個數據。 |
c.resize(num) | 重新設置該容器的大小 |
c.size() | 回容器中實際數據的個數。 |
c.begin() | 返回指向容器第一個元素的迭代器 |
c.end() | 返回指向容器最后一個元素的迭代器 |
我們可以看到,對于vector只有一種添加元素的辦法,但是對于元素的讀寫,可以直接用下標表示法,這個數組,string是完全共同的, 但是請注意,千萬不要給定一個不存在的下標,如果超出了vector變量的長度,那么毫無疑問你的,會產生嚴重錯誤!甚至可能導致 緩存區溢出!!
7、 迭代器
- 迭代器的介紹
- 迭代器類似于指針類型,它也提供了對對象的間接訪問。
- 指針是c語言中就有的東西,迭代器是c++中才有的,指針用起來靈活高效,迭代器功能更豐富些。
- 迭代器提供一個對容器對象或者string對象的訪問的方法,并且定義了容器范圍。
- 對于上面介紹的幾種標準庫類型,都有內置的迭代器操作,所謂迭代器,就是兩個地址。比如說下面的例子:
vector<int > v;
auto b=v.begin();
此時如果可以查看b的類型,你會發現其實就是個指針對象。只是其類型由編譯器給定。我們只管auto 或者 decltype就好了!,另外還有一個end()函數返回尾后迭代器,沒有什么實際意義,正如名字,是在最后一個元素的下一個位置,用于判斷是否為空的容器(begin end指向一個位置的時候)
-
下面是一些關于迭代器的操作,其中iter就是迭代器,跟指針其實沒啥區別
每種容器類型都定義了自己的迭代器類型,如vector
vector<int>::iterator iter;
語句定義了一個名為 iter 的變量,它的數據類型是 vector<int > 定義的 iterator 類型。每個標準庫容器類型都定義了一個名為 iterator 的成員,這里的 iterator 與迭代器實際類型的含義相同。
- 前面的程序用vector::iterator 改變 vector 中的元素值。每種容器類型還定義了一種名為 const_iterator 的類型,該類型只能用于讀取容器內元素,但不能改變其值。? 當我們對普通 iterator 類型解引用時,得到對某個元素的非 const。而如果我們對const_iterator 類型解引用時,則可以得到一個指向 const 對象的引用,如同任何常量一樣,該對象不能進行重寫。
for (vector<string>::const_iteratoriter = text.begin();iter != text.end(); ++iter)
cout << *iter << endl; // printeach element in text
? 使用 const_iterator 類型時,我們可以得到一個迭代器,它自身的值可以改變,但不能用來改變其所指向的元素的值。可以對迭代器進行自增以及使用解引用操作符來讀取值,但不能對該元素賦值。
- 使vector對象的迭代器失效的操作
- for中添加元素
- push_back或者改變容量的操作
記住一點:但凡是使用了迭代器的循環體,此時就不要像迭代器所屬的容器進行添加元素的操作了!!!千萬不要!!
- 迭代器的算術操作(跟指針沒差別,只是是標準庫自帶的類型)
iter + n
iter - n
iter1 - iter2
vector<int>::iterator mid = vi.begin() +vi.size() / 2;
任何改變 vector 長度的操作都會使已存在的迭代器失效。例如,在調用 push_back 之后,就不能再信賴指向 vector 的迭代器的值了!!!!!!!~
正文之后
我只能看著這個配置流口水啊!!具體的購買詳細和指導請看我另一篇文:萬元臺式機組裝養成記
后來又加了三件配個套: