1. 程式人生 > >C++與STL入門(未完)

C++與STL入門(未完)

C++與STL入門總結

寫在前面

本文是我在C語言的基礎上學習C++時的總結,學習的參考書籍是劉汝佳《演算法競賽入門經典》(第二版)。該書”C++於STL入門“章節講解了在演算法競賽中常用的一些C++特性,因此書中只是簡略的介紹了一些重要的知識點。我也是圍繞著書中所介紹的內容進行學習,對書中提及但沒能詳細講解的地方進行了一些補充和深入,因此在這裡寫一篇博文進行總結。

C++的特性

C++與C語言最大的不同是,C語言是面向過程的語言,而C++是面向物件的語言。面向過程程式設計(OPP)是一種以過程為中心的程式設計方法,它在解決問題的時候專注於發生的問題、解決的方法以及處理的步驟。而面對物件程式設計(OOP)是以事物為中心的程式設計思想,它在於將問題所涉及的物件、物件的行為等進行抽象。抽象的目的在於更好的描述問題,從而能夠更好的分析問題和解決問題。

面向過程思想解決問題採取的策略一般為自頂向下步步深入,首先將問題模組化,分解成若干的小問題,然後將問題逐個解決。因此面向過程程式設計主要的思想是模組化思想。實際上,當問題規模較小時,可以通過分析出問題解決的流程,根據流程進行模組化程式設計,從而還具有一定的優勢。

面向物件思想首先對事物進行物件化,物件還具有獨特的屬性和方法。於是對於大型問題的分析和描述上,可以不用分析整個問題的過程和解決方法。面對物件要做的便是分析各個物件受到的影響和它們能夠處理的問題(屬性的變化和具有的方法等)。

1.標頭檔案的變化

在C語言中,當需要用到標準輸入輸出時(實際上大多數情況都需要用到),需要包含標頭檔案stdio.h。在C++程式中,為了相容性,我們也可以使用stdio.h,但這並不是C++的推薦寫法,C++中推薦的標頭檔案是cstdio(注意不是#include “cstdio.h"而是“cstdio”!)。類似的,string.h也應改寫為cstring,math.h改寫為cmath,ctype.h改寫為cctype。如下程式:

#include <cstdio>

int main() {
	printf("Hello world!");
	return 0;
}

因此,如果需要在C++程式中繼續使用C語言的一些函式,更推薦的方法應該是在以前的標頭檔案前加上c,而不是繼續使用之前的.h檔案。

當然,C++也有其特有的標頭檔案,如提供輸入輸出流的iostream、提供一些常用演算法的algorithm、提供字串操作的string等。為了與C語言中的標頭檔案進行區分,在使用它們的時候都去掉了尾部.h字尾。此外,包含上述檔案的C++的標準庫都包含在“std”名稱空間(後面將詳細討論名稱空間)中。下面是利用C++標準程式庫的demo:

#include <iostream>
#include <algorithm>
using namespace std;
const int maxn = 100 + 10;
int A[maxn];
int main() {
	long long a, b;
	while (cin >> a >> b) {
		cout << min(a, b) << endl;
	}
	return 0;
}

上面的程式與純C語言程式已經有很大的區別了:
1.C++中特有的標頭檔案iostream和algorithm
2.名稱空間std的宣告
3.利用cin、cout進行輸入輸出
ps:注意這裡使用const來定義常量

2.C++標準程式庫

上面提到的iostream和algorithm屬於C++標準程式庫,所以C++標準程式庫中到底都包含些什麼呢?下面是它的定義(摘自百度百科):

C++中的標準程式庫是類庫函式的集合,其使用核心語言寫成。標準程式庫提供若干泛型容器函式物件泛型字串(包含互動和檔案I/O),支援部分語言特性常用函式,如開平方根。C++標準程式庫也吸收了ISO C90 C標準程式庫。標準程式庫的特性聲明於std名稱空間之中。

C++標準程式庫主要包含以下幾類:
1.通用(general):algorithm、iterator、memory等
2.流(stream):iostream、fstream、ostream等
3.容器(containers):map、set、vector、stack、queue等
4.數值(numerics):numeric、complex、random、valarray
5.輔助(support):exception、typeinfo、new、limits
6.執行緒(thread):thread、mutex、future、condition_variable
7.字串(strings): string、regex
8.本地化(localization):locale、codecvt
9.C標準庫(c standard library)
注:分類可能不同,沒有嚴格定義
可以看出,C++標準程式庫提供了豐富的內容,使得C++相對於C語言更加的強大和靈活,但同時也更加的複雜。
C++標準程式庫也相容所有C語言的標準庫。正如前面所說,這裡的C標準庫是前面對C庫進行加頭(c)去尾(.h)的結果。值得注意的是,C++標準程式庫中的所有識別符號都被定義於一個名為std的namespace中,連cstdio等也不例外。

3.名稱空間(namespace)

在前面我們提及,C++標準程式庫中的所有識別符號都被定義在一個名為std的namespace中。因此,我們可以把名稱空間簡單的理解為名稱的集合。
這裡空間的概念和數學中的向量空間有著異曲同工之妙,將需要研究的一類事物進行封裝,使得不同程式庫中名稱相同的元素能夠區分開來。例如:數學中向量乘法和數值乘法計算是不同的,但它們可以有相同的名字(乘法)。同理,程式庫a和程式庫b中也可以有相同的類或者函式,它們可以有相同的名稱和引數,但可以通過定義在不同的namespace來區分開。
使用名稱空間中識別符號的三種選擇(以std為例):

1.使用using namespace std
這是最簡單的一種方法,使得名稱空間std中的所有識別符號都得到了定義。因此,後面的程式中就可以使用C++標準庫中的所有內容。
值得注意的一點是,該語句是有作用範圍的。如果將該語句宣告在main函式裡面,則其他函式要使用std中的識別符號時需要再次宣告,如下例:

#include <iostream>

void HelloWorld();
int main() {
	using namespace std;
	cout << "Hello C++ !" << endl;
	HelloWorld();
	return 0;
}
void HelloWorld() {
	using namespace std;
	cout << "Hello World !" << endl;
}

當然,也可以選擇將其宣告在函式外面,相當於定義了全域性變數。

#include <iostream>
using namespace std;
void HelloWorld();
int main() {
	cout << "Hello C++ !" << endl;
	HelloWorld();
	return 0;
}
void HelloWorld() {
	cout << "Hello World !" << endl;
}

顯然,將std宣告在函式外面這種做法是很方便的,但這樣也增加了命名衝突的風險,因此更推薦的做法應該是將std宣告在函式內部。實際上,大多數情況下,大家為了圖方便都會將std宣告在函式外面,這在大型工程中是不可取的。

2.直接指定識別符號

 #include <iostream>
int main() {
	std::cout << "Hello World !" << std::endl;
	return 0;
}

這種方式直接使用名稱空間std的識別符號cout和endl,可以很大程度上的避免命名衝突。但如果該名稱空間中的識別符號在程式中大量的使用,則採用這樣的方式就不是很方便。這種情況下,採用宣告名稱空間的方式會顯得更加方便,當然,還可以採取下面這種方式。

3.使用using關鍵字

#include <iostream>
using std::cout;
using std::endl;
int main() {
	cout << "Hello World !" << endl;
	return 0;
}

這裡相當於聲明瞭std中的cout和endl,後面就可以直接使用cout和endl識別符號了。注意using關鍵字是有作用範圍的!

#include <iostream>
int main() {
   using std::cout;
   using std::endl;
   cout << "Hello World !" << endl;
   return 0;
}
void HelloCpp() {
   using std::cout;
   using std::endl;
   cout << "Hello C++ !" << endl;
}

ps:在討論完如何使用名稱空間中識別符號這個問題後,我們回到本文的第一個程式。可以看到我們使用的cstdio中的printf函式,這裡cstdio也屬於C++標準程式庫,按理說其printf識別符號應該也存在於std名稱空間,需要使用printf函式也需要按上述方法來進行宣告。事實上,C++標準確實這樣要求,但大多數編譯器並未嚴格執行這個要求。因此,我們可以直接使用cstdio中的識別符號而不需要宣告名稱空間。注意這並不是C++推薦的方式!

#include <cstdio>

int main() {
	printf("Hello world!");
	return 0;
}

4.資料型別的變化

C/C++的資料型別有兩種,預定義型別和自定義資料型別。C語言和C++中的預定義型別如下:
C:整型、實型、字元型、(空型)
C++:整型、實型、字元型、布林型、(空型)
其中,最明顯的變化是C++中增加了布林資料型別。在C語言中,我們常用整型資料來表示真假;在C++中,使用布林型變數可以讓程式變得更加清晰。

5.指標與引用

在C語言中,經典的交換變數值的程式如下,這裡採用的是地址傳遞的方式:

#include <stdio.h>
void swap(int *a, int *b);
int main() {
	int a = 3, b = 4;
	swap(&a, &b);
	printf("%d %d\n", a, b);
	return 0;
}
void swap(int *a, int *b) {
	int temp = *a; *a = *b; *b = temp;
}

在C++中,可以有更加簡潔的方式,採用引用傳遞!

#include <iostream>
using namespace std;
int main() {
	int a = 3, b = 4;
	swap(a, b);
	cout << a << " " << b << endl;
	return 0;
}
void swap(int &a, int &b) {
	int temp = a; a = b; b = temp;
}

引用是C語言中沒有的,是C++提供的特有的功能。在引用傳遞中,&不是表示取地址,而是標記一個變數是引用變數,而引用實際上就是另一個變數的別名。引用變數有如下幾點特性:
1.引用就是另一個變數的別名,不額外開闢空間
2.宣告引用的同時必須初始化,因為引用在初始化時就必須與一個變數建立聯絡。
3.引用不能再作為其它變數的引用,意思是引用自初始化之後就與一個變數建立了確定的關係,該引用不能與其他變數建立關係。如:給變數a取別名aa後,不能將aa作為另一個變數b的別名。

#include <iostream>
using namespace std;
int main() {
	int a = 1;
	int &aa = a;
	cout << "變數a的地址為:" << &a << endl;
	cout << "引用aa的地址為:" << &aa << endl;
	a=2;
	cout << "變數a的值為:" << a << endl;
	cout << "引用a的值為:" << aa << endl;
	aa = 3;
	cout << "變數a的值為:" << a << endl;
	cout << "引用a的值為:" << aa << endl;
	return 0;
}
變數a的地址為:004FFE90
引用aa的地址為:004FFE90
變數a的值為:2
引用a的值為:2
變數a的值為:3
引用a的值為:3

從上述程式碼以及執行結果可以看出:①變數a和引用aa地址相同②改變其中一個的值,另一個也會發生相應的變化。
同樣的,我們也可以對陣列和指標進行引用,示例如下:

#include <iostream>
using namespace std;
int main() {
	int a[3] = { 1,2,3 };
	int (&aa)[3] = a;
	for (int i = 0; i < 3; ++i)
		cout << a[i] << " " << aa[i] << endl;
	return 0;
}
#include <iostream>
using namespace std;
int main() {
	int a=1;
	int* ptr=&a;
	int* &ptr1 = ptr;
	*ptr1 = 2;
	cout << "a的值為:" << a << endl;
	return 0;
}

從上面的討論可以看出,引用是變數的一個別名,因此二者用起來幾乎沒有任何差別。下面將討論引用在函式引數的傳遞中起到的巨大作用。
在上面變數交換的例子中使用到了引用傳遞。引用傳遞中,函式的形式引數定義如下:
void swap(int &a, int &b) ;
函式呼叫時傳遞引數形式如下:
swap(a, b);
在函式呼叫時,函式的形參就會被當作實參的別名來使用。因此,在函式內部對引用所進行的所有操作都等同於對變數進行的操作。當然,使用指標也可以 實現這樣的效果,但引用會使得程式碼更加的簡潔,並且不需要額外的記憶體消耗。
當然,引用傳遞也可以傳遞陣列。在C語言中,傳遞陣列實際上就是傳遞陣列的地址,函式中形參可以是指標,也可以是陣列。但實際上,二者的作用都是一樣的。下面是C語言中傳遞陣列的兩種方式:

#include <stdio.h>
void swap1(int a[2]);
void swap2(int* a);
int main() {
	int a[2] = { 1, 2 };
	printf("Outside: Length=%d ", sizeof(a) / sizeof(int));
	printf("{ %d %d }\n", a[0], a[1]);
	swap1(a);
	printf("{ %d %d }\n", a[0], a[1]);
	swap2(a);
	printf("{ %d %d }\n", a[0], a[1]);
	return 0;
}
void swap1(int a[2]) {
	int temp = a[0]; a[0] = a[1]; a[1] = temp;
	printf("swap1  : Length=%d ", sizeof(a)/sizeof(int));
}
void swap2(int* a) {
	int temp = a[0]; a[0] = a[1]; a[1] = temp;
	printf("swap2  : Length=%d ", sizeof(a) / sizeof(int));
}
Outside: Length=2 { 1 2 }
swap1  : Length=1 { 2 1 }
swap2  : Length=1 { 1 2 }

可以看出,無論形參是陣列還是指標,函式中均不能通過sizeof來獲取陣列的大小。並且,形參中定義的陣列大小並不重要,因為函式傳入的是陣列的地址,而地址中不攜帶長度資訊。下面是引用傳遞的例子:

#include <iostream>
using namespace std;
void swap1(int(&a)[2]);
int main() {
	int a[2] = { 1, 2 };
	cout << "Outside: Length=" << sizeof(a) / sizeof(int) << " ";
	cout << "{ " << a[0] << " " << a[1] << " }" << endl;
	swap1(a);
	cout << "{ " << a[0] << " " << a[1] << " }" << endl;
	return 0;
}
void swap1(int(&a)[2]) {
	int temp = a[0]; a[0] = a[1]; a[1] = temp;
	cout << "swap1  : Length=" << sizeof(a) / sizeof(int) << " ";
}
Outside: Length=2 { 1 2 }
swap1  : Length=2 { 2 1 }

由於是引用傳遞,swap1函式中的引用a就是陣列a的別名,因此不僅可以利用引用來更改陣列中的值,還可以通過引用獲得陣列的大小引用傳遞陣列時也就必須指明陣列的大小

最後,討論引用作為函式的返回值的情況。我們都知道,函式內的臨時變數在函式執行完之後便會被銷燬。因此,如果我們把臨時變數的引用作為函式的返回值,並將其作為新的引用的初始值,則新的引用也將被銷燬。

#include <iostream>
using namespace std;
int &swap1();
int aa();
int main() {
	int &a=swap1();
	cout << a << endl;
	aa();
	cout << a << endl;
	return 0;
}
int &swap1() {
	int temp = 1;
	return 1;
}
int aa() {
	return 1;
}
1
-858993460

當然,也可以使用變數來接收函式的返回值,這樣函式返回的值就會利用該變數進行儲存而不會被銷燬。但是這樣做和直接讓函式返回值沒有任何區別,我們讓函式返回引用的目的就是為了讓其返回變數本身,而不是返回變數的值。
因此,當將引用作為函式的返回值時,函式應當返回全域性變數而不是區域性變數

6.字串(string)

C語言提供了string.h來方便程式設計師處理字串,string.h中主要包含以下用於字串處理的函式:

C++中除了支援C中的字元陣列外,還提供了一個更加強大的string類。但由於string類涉及太多面向物件的內容,這裡只作簡單介紹,在後面會進行詳細討論。
(1)字串建立
C++對string的 建構函式實現了多個過載,因此有多種方法建立並初始化一個字串,下面是幾個常用的建立方式。

#include <iostream>
#include <string>
using namespace std;
int main() {
	string s1;//建立空串
	string s2("Hello World!");          //以cstring作為初值
	string s3(s2);                      //以string作為初值,相當於拷貝
	string s4(s2, 5);                   //取string第5個字元之後的所有字元
	string s5("Hello World!", 5);       //取cstring的前5個字元
	string s6(s2, 6, 5);                //取string第6個字元之後的5個字元
	string s7("Hello World", 6, 5);     //取cstring第6個字元之後的5個字元
	cout << "s1 = " << s1 << endl;
	cout << "s2 = " << s2 << endl;
	cout << "s3 = " << s3 << endl;
	cout << "s4 = " << s4 << endl;
	cout << "s5 = " << s5 << endl;
	cout << "s6 = " << s6 << endl;
	cout << "s7 = " << s7 << endl;
	return 0;
}
s1 =
s2 = Hello World!
s3 = Hello World!
s4 =  World!
s5 = Hello
s6 = World
s7 = World

這裡需要注意的是s4和s5的不同,分別以string和cstring作為源建立string時,兩種過載的第二個引數意義是不同的,前者為起始位置,後者為字元數。

(2)字元元素存取
C++提供了三種方式對string中的字元進行索引,分別為:下標索引 [ i ],at( i )訪問,back()/front()訪問首字元和末字元。

#include <iostream>
#include <string>
using namespace std;
int main() {
	string s("Hello World!");//以cstring作為初值
	cout << s[0] << endl;
	cout << s.at(0) << endl;
	cout << s.front() << endl;
	cout << s.back() << endl;
	return 0;
}
H
H
H
!

(3)字串賦值
string類過載了“=”操作符,因此可以直接用"="進行賦值,此外,C++還提供了更加靈活的assign成員函式來對字串進行賦值。

#include <iostream>
#include <string>
using namespace std;
int main() {
	string s1, s2, s3, s4, s5, s6;
	s1 = "Hello World !";//=cstring
	s2 = s1;			 //=string
	s3.assign(s1);		 //assign(string)
	s3.assign("Hello World !");//assign(cstring)
	s4.assign("Hello World !", 5);//assign(cstring, n)	
	s5.assign(s1, 5);		//assign(string, pos)
	s6.assign(12, '-');		//assign(n, char)
	cout << s1 << endl;
	cout << s2 << endl;
	cout << s3 << endl;
	cout << s4 << endl;
	cout << s5 << endl;
	cout << s6 << endl;
	return 0;
}
Hello World !
Hello World !
Hello World !
Hello
 World !
------------

同使用建構函式建立字串時相同,這裡的s4和s5也得到了不同的結果,因此對於ctring和string,assign實現了不同的過載,意義同構造函式。

(4)字串操作
C++提供了許多對字串進行操作的方法,包括增、刪、查詢、替換、交換、屬性獲取等許多方便的功能。下面就幾個常用的方法進行簡要的總結。
增刪操作
string過載了”+=操作符",因此可以利用“+=”來增長字串。此外,C++還提供了append()和push_back()來對字串進行增操作,erase()來對字串進行減操作,clear()來對字串進行清空操作。


            
           

相關推薦

C++STL入門

C++與STL入門總結 寫在前面 C++的特性 1.標頭檔案的變化 2.C++標準程式庫 3.名稱空間(namespace) 4.資料型別的變化 5.指標與引用 6.字串(string) 7.結構體與

003 PythonC語言的區別

特點 .com 裏的 mar dom info pytho alt 溢出 #寫在前面的話:重點記錄Python的特點 Python特點: 1. 無分號斷句 2. 不用擔心溢出問題 3. if-else的用法不同 #if或else後面都要添加冒號

前端HTML css 整理

簽名 跳轉 doc emp 分類 for ... 小寫 tar HTML 中的標簽存放於文本文件中 需要按照以下固定的文檔結構組織:<!DOCTYPE HTML><html> <head>頭部相關信息 </head>

Ionic angularjs App安裝使用總結

第一部分 安裝時總結 一、win系統下nodejs安裝及環境配置 第一步:下載nodejs,官網:http://nodejs.org/download/ 第二步:安裝nodejs 下載完成之後,雙擊"node-v0.10.28-x86.msi",開始安裝nodejs

Python之旅.第四章.模塊包.總結待遇

standard 後綴 att 擔心 lse 綁定 做的 業務 搜索 一、模塊 模塊: 一系列功能的集合體,在python中一個py文件就是一個模塊,模塊名就是py文件的文件名; 模塊的好處: 1.減少重復的代碼 2.拿來主義 定義模塊: 就是創建一個py文件;

c# winform 實現打印功能

ble preview raw using ntp review winform setup print 1.打印控件介紹(Document屬性設置為PrintDocument1;ShowDialog()方法顯示對話窗) PrintDialog控件(打印會話):用於選擇打印

3.3常用知識-索引排序

聚集索引 主鍵自增 主鍵 建立索引 函數 3.3 存儲 bsp 排序。 1.索引與排序的關系   經過多番嘗試,我發現,直接select * from table 默認是會按聚集索引來排序的。   那如果order by column ,column中有非聚集索引,排序用使

學習筆記之——HOG、LBPHaar特徵待續

本博文為HOG、LBP與Haar運算元的學習筆記。   方向梯度直方圖HOG 主要參考博文如下: https://blog.csdn.net/wjb820728252/article/details/78395092(這篇博文翻譯水平真的太爛了,可以參考裡面給的原連結)

C語言 線性表的操作~

#include <stdio.h> #include <malloc.h> typedef struct{ int *elem; //基地址 int length; int listsize; }Seqlist;//定義Seq這個新的資料

SpringBoot:SpringBoot資料訪問待續

1、簡介 對於資料訪問層,無論是SQL還是NOSQL,SpringBoot預設採用整合SpringData(SpringData是Spring 的一個子專案。用於簡化資料庫訪問,支援NoSQL 和 關係資料儲存。其主要目標是使資料庫的訪問變得方便快捷)的方式進行

華為IP基礎快速入門-P15-BGP原理

RIP\OSPF都屬於IGP,是工作在AS內的路由協議 BGP是EGP的典型代表 和RIP很像,傳遞是路由資訊 BGP是建立的對等體關係 AS_Path起到直到路徑、判斷優劣、防環的作用(當收到自己的號碼時候,就知道發現環路) BGP不用直連才可以,只要IP可達,並且通過TCP179

華為IP基礎快速入門-P36-PIM原理

成員會向RP申請,同一個組播組只能對映在同一個RP上面。 靜態RP的缺點:需要手動配置 BSR: C-BSR是BSR的候選者,要競選BSR,BSR勝利者會告訴全網自己是BSR C-RP就會向BSR報告自己的資訊,然後BSR就會彙集起來成為一個集合,然後把集合傳送給全網,然後組播組就會進行

C++對於bit的操作 bit直接賦值 及其他技巧

最近做一個壓縮程式,需要直接操作bit這一單位,但是C和C++這兩個號稱面向底層的語言竟然沒有提供對於bit的直接支援,最小單位是bit。。 後來發現了一個間接操作bit的方法,就是湊成一個int來讀或者寫,配合上<<和>>和&等來進行操作

C/C++常見面試知識點總結附面試真題----20180919更新

以下內容部分整理自網路,部分為自己面試的真題。 第一部分:計算機基礎 1. C/C++記憶體有哪幾種類型? C中,記憶體分為5個區:堆(malloc)、棧(如區域性變數、函式引數)、程式程式碼區(存放二進位制程式碼)、全域性/靜態儲存區(全域性變數、static

【Python】正則表達式1

pes mmu get regular rop 則表達式 line out github 1、正則表達式唯一的用途就是在文本中匹配和尋找模式,模式可以簡單,也可以復雜。 2、Regexr 這個網站很個性的就是,有一個community標簽,打開後可以看到評分由高到低

Python基礎day-11[內置函數,遞歸,匿名函數]

oat 讀寫 磁盤 自動 信息 map() instance 冒號 匿名 內置函數: abs() : 返回數字的絕對值。參數可以是整數或浮點數,如果參數是復數,則返回復數的模。 print(abs(0.2)) print(abs(1)) print(abs(-4)) pr

centos中編譯安裝nginx+mysql +php

net conf ftw tar 解壓 ocs org sql nbsp 參考地址:http://www.cnblogs.com/htian/p/5728599.html 去官網找到PCRE,並下載http://www.pcre.org/wget ftp://ftp.csx

elasticsearch aggregation 過程

elasticsearch aggregation 過程在查詢過程中,ES是將整個查詢分成幾個階段的,大體如下:QueryPhaserescorePhasesuggestPhaseaggregationPhaseFetchPhase對於全文檢索,可能還有DFSPhase。從源代碼QueryPhase 類可以看

Python第四天

刪除 back del -c remove strong 數據類型 切片 ron 一、拾遺 1、在Python中數據又稱為對象,每創建一個對象都會創建三個屬性: (1)身份:id   is 用來比較id,id一樣,type和value肯定一樣 (2)類型:type 

低俗小說

個人 出租車 事情 回來 自己 something 是個 round 提醒 簡單介紹:這個筆記是看低俗小說時候跟著刷的劇情,有事耽擱沒有刷完,整部電影充滿了細節 pulp fiction 低俗小說開場:一對情侶準備搶劫咖啡店,搶劫咖啡店的顧客錢包1.兩個殺手出場,談到大佬馬