C++............................................................................................................................................................................ 2前端
STL............................................................................................................................................................................ 30ios
C++
1、C++語言的背景介紹
1.C++語言的江湖地位
C Java Objective-C C++
2.歷史人物
Ken Thompson,B語言之父,UNIX發明人之一,1943-活着。
Dennis Ritchie,C語言之父,UNIX之父,黑客之父,1941-2011。
Bjarne Stroustrup,C++之父,1950-活着。
3.C++之父的貢獻
1979年4月,分析UNIX系統因爲內核分佈而形成的網絡流量,試圖將simula語言的面向對象特性和B語言的高效性結合起來。
1979年10月,Cpre預編譯器,爲C語言增長了一些相似simula的機制。
1983年,C with Classes,後來被改名爲C++。
- simula的類
- algol的操做符重載
- BCPL的註釋風格——「//」
- ADA的模板和名字空間
- Clu/ML的異常
1985年,CFront 1.0發佈
1987年,GNU C++發佈
1990年,Borland C++發佈
1992年,Microsoft C++發佈
1998年,ISO標準,C++98
2003年,對C++98進行修訂,C++03
2011年,大幅升級,C++11(C++0x)
2014年,對C++11作了部分擴展,C++14
2、無處不在的C++
遊戲、科學計算、網絡和分佈式應用、操做系統和設備驅動、嵌入式、編譯器、虛擬機和腳本引擎,等等。
3、C++的競爭對手
Java、C#、C
同時具有高性能和良好抽象建模能力。
4、更好C
1.純編譯語言,與C語言具備徹底相同的編譯模型。
2.強類型語言,比C語言的類型檢查更加嚴格。
i = 10;
i = "hello";
int i;
3.去除了一些C中很差的特性。如函數的隱式聲明等。
4.增長了一些新的特性:面向對象、操做符重載、異常和泛型編程。
5.和C相比C++更加適合從事大型系統的開發。
5、第一個C++程序
1.編譯命令
g++,也能夠用gcc,可是要加上-lstdc++
2.擴展名
.cpp/.cc/.C/.cxx,也能夠用.c,可是用gcc命令編譯時須要加上-x c++
3.頭文件
#include <iostream>
4.I/O對象
cin:標準輸入(stdin)
cout:標準輸出(stdout)
cerr:標準錯誤(stderr)
<<:插入
>>:提取
a*b
*p
5.名字空間
全部標準C++語言中的函數、對象、類型等都在std名字空間中。
6、名字空間
1.爲何?
1)避免產生名字衝突。
2)將基於邏輯的劃分和基於物理的劃分獨立開。
2.什麼是?
namespace 名字空間名 {
名字空間成員1;
名字空間成員2;
...
}
namespace ns {
int var = 0;
void fun (void) { ... }
}
3.名字空間合併
namespace 名字空間名 {
名字空間成員1;
名字空間成員2;
}
namespace 名字空間名 {
名字空間成員3;
}
a.cpp:
namespace ns {
int var = 0;
void fun (void) { ... }
}
b.cpp:
namespace ns {
struct type { ... };
}
4.使用名字空間中的名字
1)經過做用域限定操做符「::」
2)名字空間指令
using namespace 名字空間名;
該條指令之後的代碼中,對於所使用的名字空間中的名字能夠直接引用,前提是沒有衝突。
3)名字空間聲明
using 名字空間名::成員名;
將指定名字空間中的特定成員引入當前做用域。
using ns::var;
using ns::fun;
using ns::type;
4)無名名字空間
C++會將不屬於任何有名名字空間中的名字通通放到無名名字空間中。對於無名名字空間中的名字能夠直接經過「::」訪問。
5)名字空間嵌套
7、結構、聯合和枚舉
struct Student {
...
};
struct Student s1 = { ... }; // C
Student s1 = { ... }; // C++
union Array {
...
};
union Array a1; // C
Array a1; // C++
enum COLOR { ... };
enum COLOR c1; // C
COLOR c1; // C++
1.C++的結構體能夠定義函數,並且在這些函數中能夠直接訪問結構體的字段(成員變量)。
2.C++中定義匿名聯合。
3.C++中的枚舉不能和整型通用。
8、字符串
C++語言提供了專門的字符串類型:string。
9、布爾類型
bool:true/false
1、重(chong)載(overload)
1.同一個做用域中,函數名相同,參數表不一樣的函數,構成重載關係。
2.調用函數時,根據實參與形參的類型匹配狀況,選擇一個肯定的重載版本,這個過程稱爲重載解析。
3.經過函數指針指向具備重載關係的函數,所選擇的重載版本由該函數指針的類型決定。
4.C++換名:C++編譯器會按照必定的規則,將函數的參數表信息與函數的原始名混合編碼,造成一個新的函數名。所以具備重載關係的函數,雖然其原始名同樣,可是由於其參數表的不一樣,最後編譯生成的實際函數名是不一樣的。
5.經過extern "C"能夠要求C++編譯器按照C的方式處理函數接口,即不換名,固然也就重載。
extern "C" int add (int a, int b);
extern "C" int sub (int a, int b);
或
extern "C" {
int add (int a, int b);
int sub (int a, int b);
}
若是包含extern "C"聲明指定符的文件也須要被C編譯器處理,爲了防止C編譯器由於沒法識別extern "C"而致使報錯:
#ifdef __cplusplus
extern "C" {
#endif
...
#ifdef __cplusplus
}
#endif
只有在C++編譯器中__cplusplus宏纔有定義,在C編譯器中該宏無定義。
2、缺省參數
1.能夠爲函數的參數指定缺省值,調用函數時若未指定實參,則與該實參相對應的形參取缺省值。
2.函數的缺省參數是在編譯階段解決的,所以只能用常量、常量表達式或者全局變量等非局部化數值做爲缺省參數。
3.若是函數的聲明和定義分開書寫,那麼該函數的缺省參數只能出如今聲明部分,定義部分不能指定缺省參數,可是能夠經過註釋提升代碼的可讀性。
4.若是函數的某一個參數帶有缺省值,那麼該參數後面的全部參數必須都帶有缺省值。
5.不要由於使用缺省參數而致使重載歧義。
3、啞元
1.只指定類型而不指定名稱的函數參數,謂之啞元。
2.啞元主要應用於版本升級過程當中的向下兼容和特殊的操做符重載等場合。
4、內聯
1.內聯就是用函數已被編譯好的二進制代碼,替換對該函數的調用指令。
2.內聯在保證函數特性的同時,避免了函數調用的開銷。經過犧牲代碼空間,贏得了運行時間。
3.內聯會使可執行文件的體積和進程代碼區的內存變大,所以只有頻繁調用的簡單函數才適合內聯。稀少被調用的複雜函數,調用開銷遠小於其執行開銷,由內聯而得到的時間性能改善,不足以抵消空間性能的損失,故不適合內聯。
4.遞歸函數不能內聯。
5.經過inline關鍵字能夠顯式地請求編譯器將某個函數處理爲內聯函數。
6.inline關鍵字僅僅表示一種對函數實施內聯優化的指望,但該函數是否真的會被處理爲內聯,還要由編譯器的優化策略決定。
7.多數現代編譯器已經把內聯做爲一種缺省的優化機制,即便不是顯式使用inline關鍵字,只要該函數符合內聯優化的條件,編譯器也會自動將其處理爲內聯函數。
5、動態內存分配
1.C中動態內存分配函數在C++中能夠繼續使用。
#include <cstdlib>
void* malloc (size_t size);
void* calloc (size_t nmemb, size_t size);
void* realloc (void* ptr, size_t size);
void free (void* ptr);
typedef unsigned int size_t;
2.new/delete運算符
int a = 0;
sizeof (a=5);
cout << a << endl; // 0
1)動態分配/釋放單個變量
int* p = new int;
int* p = new int (123);
delete p;
2)動態分配/釋放數組變量
int* p = new int[5] { ... };
delete[] p;
3)動態分配/釋放高維數組
int (*prow)[4] = new int[3][4];
delete[] prow;
4)new在分配內存失敗的狀況下,不是返回NULL指針,而是拋出bad_alloc異常。
5)定位分配
new (地址) 類型[大小]
int* pn = new (pool) int (123);
1、引用
1.引用的基本特性
1)引用即別名
聲明一個標識符爲引用,即表示該標識符可做爲另外一個有名或無名對象的別名。
int a = 10;
int& b = a; // b是a的一個引用,即b是a的一
// 個別名
++b;
cout << a << endl; // 11
int& c = b; // c是a的另外一個引用
++c;
cout << a << endl; // 12
常引用的目標不可修改,只能讀取。
int const& d = c;
++d; // ERROR
在C++中,無名對象(字面值常量、臨時變量)都被視做右值,只能經過常引用引用之。
int const& a = 10; // 純右值
int const& a = x + y; // 將亡右值
2)引用必須初始化
int a = 10;
int a;
int* p = NULL;
int* p;
int& r; // ERROR
int& r = a;
3)沒法定義一個什麼都不引用的引用。
int* p = NULL; // 什麼都不指向的指針
int& r = NULL; // ERROR
4)引用一經初始化便不能再引用其它對象。
int a;
int& r = a;
int b;
r = b; // b -> a
2.引用型參數
1)函數的形參是實參的別名
能夠將函數的形參聲明爲引用形式,該形參在參數傳遞過程當中由對應的實參初始化,併成爲該實參的別名。
2)在函數中修改實參的值
經過引用型形參,能夠在函數體內部修改調用者實參的值,成爲除返回值和指針參數之外,第三種由函數內部向函數外部輸出數據的途徑。
3)避免對象複製的開銷
經過引用傳遞參數,形參只是實參的別名而非其副本,這就避免了從實參到形參的對象複製,這對於具備複雜數據結構的參數而言能夠提升其傳參的性能。
若是函數只是讀取引用型參數的目標對象,那麼最好經過常引用接收實參,防止在函數中意外地修改實參變量的值。
3.引用型返回值
foo () = 10;
++foo ();
1)返回左值
2)函數的局部變量只具備函數級甚至塊或語句級的生命週期,函數一旦返回,全部的局部變量即刻銷燬,即便經過返回值得到了對它們的引用,其目標也將是未定義的。所以不要從函數中返回對局部變量的引用,而返回全局、靜態、成員、堆變量的引用是安全的。
int* foo (void) {
int a = 10;
...
return &a;
}
printf ("%d\n", *foo ());
int b = 20;
int c = b + *foo ();
4.雖然引用是經過指針實現的,可是在高級語言層面引用和指針仍是具備若干不一樣的特性
1)指針能夠不初始化,其目標能夠在初始化後隨意變動;可是引用必須初始化,並且一旦初始化就沒法變動其目標
int x = 10, y = 20;
int* p; // p沒有初始化
p = &x; // p指向x
p = &y; // p指向y
--------------------
int x = 10, y = 20;
int& r = x; // r必須初始化
r = y; // r引用不了y
2)能夠定義空指針,即什麼也不指向的指針,可是不能定義空引用,引用必須有所引用,不然引用將失去意義。
int* p = NULL;
int& r = NULL; // ERROR
3)能夠定義指向指針的指針,可是沒法定義引用引用的引用。
int x = 10;
int* p = &x; // 一級指針
int** pp = &p; // 二級指針 // 指向指針的指針
------------------
int x = 10;
int& r = x;
int&& rr = r; // ERROR
C++2011中相似「int&&」的類型是合法的,可是它表示右值引用,而非二級引用。
4)能夠定義引用指針的引用,可是沒法定義指向引用的指針。
int x = 10;
int* p = &x;
int*& q = p; // q是p的別名
cout << *q << endl; // 10
----------------
int x = 10;
int& r = x;
int&* s = &r; // ERROR
int* s = &r; // OK
5)能夠定義存放指針的數組,可是沒法定義存放引用的數組。能夠定義引用數組的引用。
int a = 10, b = 20, c = 30;
int* p[3] = {&a, &b, &c}; // 指針數組
-----------------
int a = 10, b = 20, c = 30;
int& r[3] = {a, b, c}; // ERROR
-----------------
int a[3] = {10, 20, 30};
int (&r)[3] = a; // 數組引用
cout << r[0] << endl; // 10
r[1]++;
cout << a[1] << endl; // 21
5.函數指針和函數引用
int x = 10;
int* p = &x;
int& r = x;
*p = 20;
r = 20;
----------------------------
int func (double); // 函數
int (*pfunc) (double) = &func; //函數指針
int (&rfunc) (double) = func; //函數引用
(*pfunc) (3.14);
rfunc (3.14);
2、顯式(強制)類型轉換
1.C風格的顯式類型轉換
(目標類型)源類型變量
int i = 1234;
char c = (char)i;
2.C++風格的顯示類型轉換
1)靜態類型轉換
static_cast<目標類型> (源類型變量)
編譯器會對源類型和目標類型作相容性檢查,檢查不經過報錯。
A.若是在源類型和目標類型之間,至少有一個方向能夠作隱式類型轉換,那麼這兩個類型就是相容類型。
short x = 10;
void* v = &x;
short* p = static_cast<short*> (v);
--------------------
short x = 10;
short* v = &x;
int* p = static_cast<int*> (v); // ERROR
B.若是從源類型到目標類型存在自定義的轉換規則(類型轉換構造函數/類型轉換運算符函數),那麼它們也能夠被稱爲相容類型。
2)動態類型轉換
dynamic_cast<目標類型> (源類型變量)
用於具備多態性的父子類型的指針或引用。
3)去常類型轉換
const_cast<目標類型> (源類型變量)
去除指針或引用的常屬性。
int x = 10;
int const* cp = &x;
int* p = const_cast<int*> (cp);
char* q = const_cast<char*> (cp); // 錯誤
char* q = (char*)cp; // OK,風險
4)重解釋類型轉換
reinterpret_cast<目標類型> (源類型變量)
轉換任意類型的指針或引用。
在任意類型的指針和整型之間轉換。
3、面向對象
1.爲何要面向對象
1)相比於分而治之的結構程序設計,強調大處着眼的面向對象程序設計思想,更適合於開發大型軟件。
2)得益於數據抽象、代碼複用等面向對象的固有技術,軟件開發的效率得到極大的提高,成本卻大幅下降。
3)面向對象技術在數據庫、網絡通訊、圖形界面等領域的普遍應用,已催生出各類設計模式和應用框架。
4)面向對象已經成爲現代計算機程序設計語言的發展潮流,不但幾乎全部新誕生的語言都是面向對象的,並且不少傳統的結構化語言也在不斷地引入面向對象的機制。
2.什麼是面向對象
1)萬物皆對象。
2)把大型軟件當作一個由對象組成的社會。
3)對象擁有足夠的智能,可以理解來自其它對象的信息,並以適當的方式做出反應。
4)對象可以從高層對象繼承某些特性,並容許低層對象從本身繼承某些特性。
5)編寫程序的過程就是一個描述對象的過程,最終是問題域和解域得到完美的統一。
6)面向對象的四大要素:封裝、繼承、多態、抽象。
1、類和對象
1.經過屬性和行爲描述具體的對象,其中屬性表示對象的靜態特徵,而行爲則表示對象的動態特徵。
2.擁有相同屬性和行爲的對象被分爲一組,即一個類。
屬性 行爲
狗 犬種 進食
犬齡 睡眠
體重 玩耍
毛色
學生 姓名 吃飯
年齡 睡覺
學號 學習
手機 品牌 接打電話
型號 收發短信
價格 上網
玩遊戲
3.類即邏輯抽象
1)簡單類型:只能表示一個屬性(變量)。
2)數組類型:能夠表示多個屬性(元素),可是類型必須相同。
3)結構體類型:能夠多個類型不一樣的屬性(字段),但缺乏對行爲(函數)的描述。
4)類類型:既能夠表示多個不一樣類型的屬性(成員變量),同時也能夠表示多個不一樣的行爲(成員函數)。
現實世界 邏輯空間 虛擬世界
小狗 -> 狗類 -> 狗對象
真實對象 抽象描述 邏輯對象
^
OOP
2、類的定義與實例化
1.類的主要內容
1)成員變量:描述對象的各類屬性。
2)成員函數:描述對象的各類行爲。
3)構造函數:對對象作初始化。
4)析構函數:對對象作終結化。
5)訪控屬性:決定成員的訪問特性。
public(struct缺省) - 公有,誰均可以訪問;
private(class缺省) - 私有,只有本身能夠訪問;
protected - 保護,只有本身和本身的子類能夠訪問。
6)繼承方式與基類:繼承。
class Student {
public:
// 吃飯
void eat (string const& food) { ... }
// 睡覺
void sleep (int time) { ... }
// 學習
void learn (string const& course) { ... }
private:
string m_name; // 姓名
int m_age; // 年齡
int m_no; // 學號
};
2.構造函數
1)函數名與類名相同,且沒有返回類型。
2)構造函數在建立對象時被系統自動調用。
A.直接定義變量,棧對象;
B.用new操做符建立對象,堆對象。
3)構造函數在對象整個生命期內,必定會被調用,且僅被調用一次。
4)構造函數負責對成員變量進行初始化,分配必要的資源,設置對象的初始狀態。
3、構造函數與初始化表
1.構造函數能夠重載
1)構造函數經過參數表的差異化以重載的形式提供不經過的初始化方法。
2)重載的構造函數經過構造實參的類型選擇匹配。
3)使用缺省參數能夠減小構造函數重載版本的數量。注意避免重載衝突。
2.具備特殊意義的構造函數
1)缺省構造函數
可以以無參的方式被調用的構造函數稱爲缺省構造函數。
缺省構造函數表示對象的默認初始化狀態。
若是一個類中沒有定義任何構造函數,那麼編譯器就會自動爲其生成一個缺省構造函數。對於基本類型的成員變量不作任何處理,而對於類類型的成員變量,調用其相應類型的缺省構造函數。
2)類型轉換構造函數
凡是能夠經過一個參數調用的構造函數都屬於類型轉換構造函數。如:
class 目標類型 {
目標類型 (源類型 const& 引用) { ... }
};
經過該構造函數將源類型的對象隱式或顯式地轉換爲目標類型的對象。若是但願該轉換必須顯式完成,能夠在該類型轉換構造函數前面加上explicit關鍵字。
3)拷貝構造函數
當用一個對象構造與它同類型的副本對象時,編譯器會經過拷貝構造函數完成該副本對象的初始化。如:
class 類 {
public:
類 (類 const& 引用) { ... }
};
若是一個類沒有定義拷貝構造函數,編譯器就會自動提供一個缺省的拷貝構造函數,該函數對於基本類型的成員變量,按照字節複製,而對於類類型的成員變量,則會調用其相應類型的拷貝構造函數,構造其副本。
多數狀況下不須要本身定義拷貝構造函數,編譯器提供的缺省版本已經足以知足要求。但在某些特殊狀況下,須要自行定義以彌補缺省版本的不足。
拷貝構造會下降應用程序的性能,設計時儘可能避免,好比經過引用傳遞參數。
編譯器會經過必要的優化策略,減小拷貝構造的機會。經過-fno-elide-constructors選項能夠關閉此優化特性。
自定義構造函數 系統定義構造函數
無 缺省構造函數
缺省拷貝構造函數
非拷貝構造函數 缺省拷貝構造函數
拷貝構造函數 無
3.初始化表
1)經過在類的構造函數中使用初始化表,指明該類的成員變量如何被初始化。
2)類的類類型成員變量,要麼在初始化表中顯式初始化,要麼經過相應類型的缺省構造函數初始化。
3)類的常量型和引用型成員變量,必須在初始化表中顯式初始化,不能在構造函數體中賦初值。
4)類的成員變量按其在類中被聲明的順序依次初始化,而與其在初始化表中的排列順序無關。
程序員寫:
Student s ("張飛", 25);
編譯後的機器指令至關於以下:
從棧內存中分配sizeof (Student)字節
調用Student::Student ("張飛", 25)函數初始化
程序員寫:
Student* s = new Student ("張飛", 25);
被編譯器處理爲:
Student* s = malloc (sizeof (Stduent));
調用Student::Student ("張飛", 25)函數初始化(s->Student ("張飛", 25)/
Student::Student (s, "張飛", 25))
1、C++對象模型
class Student {
public:
Student (char const* name, int age) {
strcpy (m_name, name);
m_age = age;
}
void print (void) {
cout << m_name << "," << m_age
<< endl;
}
private:
char m_name[256];
int m_age;
};
Student s1 ("張飛", 25);
1.C++的對象模型和C的結構模型沒有任何區別,包括成員的佈局,以及對齊補齊的規則。
2.對象中只有成員變量,沒有成員函數。類的成員變量在該類的每一個對象中都一份獨立的拷貝。可是類的成員函數只有一份,且爲該類的全部對象共享。
3.爲了在一個函數中區分出不一樣的調用對象,編譯器會爲每一個成員函數提供一個隱藏的指針參數——this指針——指向調用該成員函數的對象。在成員函數中對全部成員變量的訪問,以及對其它成員函數的調用,實際上都是經過this指針完成的。
4.類的構造函數中一樣具備this指針參數,指向這個正在被構造的對象。
class B;
class A {
B m_b;
};
class B {
A m_a;
};
A a; sizeof (A) ?
5.顯式使用this指針的場合
1)解決成員變量的名字衝突;
2)從成員函數中返回調用對象的自引用;
3)經過成員函數實現對象間的交互;
4)在成員函數函數銷燬調用對象自身。
2、常函數
1.在類成員函數的參數表以後,函數體的左花括號以前,加上const關鍵字,該成員函數的this指針即具備常屬性,這樣的成員函數被稱爲常函數。
2.在常函數內部,由於this指針被聲明爲常量指針(即目標只讀的指針),因此沒法修改爲員變量的值,除非該成員變量被mutable關鍵字修飾。
3.常對象只能調用常函數,很是對象既能夠調用很是函數也能夠調用常函數。原型相同的常函數和很是函數能夠構成重載關係。
3、析構函數
1.析構函數是類的特殊的成員函數
1)析構函數的函數名就是在類名前面加「~」;
2)析構函數沒有返回類型;
3)析構函數沒有參數
4)析構函數不能重載
2.析構函數在對象被銷燬時自動調用
1)局部對象的析構函數由其所在最小做用域的右花括號調用;
2)堆對象的析構函數被delete運算符調用;
3)全局對象的析構函數,被進程加載器調用。
3.缺省析構函數
若是一個類沒有定義析構函數,編譯器提供一個缺省析構函數。該函數對於基本類型的成員變量什麼也不作,而對於類類型的成員變量,則會調用其相應類型的析構函數。
4、拷貝構造和拷貝賦值
1.缺省方式的拷貝構造和拷貝賦值,對包括指針在內的基本類型成員變量按字節複製,致使淺拷貝問題。
2.爲了得到完整意義上的對象副本,必須本身定義拷貝構造函數和拷貝賦值運算符函數,針對指針型成員變量作深拷貝。
3.拷貝賦值運算符函數的實現
1)避免自賦值;
2)分配新資源;
3)釋放舊資源;
4)拷貝新內容;
5)返回自引用。
1、靜態成員
1.屬於類而非對象
1)靜態成員變量不包含於對象實例中,具備進程級的聲明週期
2)靜態成員函數沒有this指針,也沒有常屬性
3)靜態成員函數只能訪問靜態成員(變量或函數),非靜態成員函數既能夠訪問靜態成員,也能夠訪問非靜態成員。
2.靜態成員也受訪問控制的約束。
3.靜態成員變量必須在類的外部定義或初始化,靜態成員函數既能夠在類的外部也能夠在類的內部定義。
2、成員指針
經過類型的約束,表達指向類特定類型成員的指針。
3、操做符(運算符)重載
1.操做符標記
1)單目操做符:只有一個操做數的操做符。
-/++/--/&/*/->/!/~/()/類型轉換,等等
2)雙目操做符:有左右兩個操做數的操做符。
算術運算:*///%/+/-
關係運算:>/>=/</<=/==/!=
邏輯運算:&&/||
位運算:&/|/^/<</>>
賦值和複合賦值:=/+=/-=/*=//=/...
下標運算:[]
int a[5] = { ... };
cout << a[3] << endl;
3)三目運算符:包含三個操做數的操做符。
條件運算:A ? B : C
2.操做符函數
1)在特定條件下,編譯器有能力把一個由操做數和操做符組成的表達式,解釋成爲一個全局或者成員函數調用,該全局或者成員函數就被成爲操做符函數。
複數(3+4i)
2)經過定義操做符函數,能夠實現針對自定義類型的運算法則,並使之與內置類型具備一致的語義。
3.雙目操做符
L#R
-> L.operator# (R) // 成員,左調右參
-> ::operator# (L, R) // 全局,左一右二
1)運算類:左右操做數均可覺得左值或右值,表達式的值必須是右值。
友元:能夠經過friend關鍵字,把一個函數或者類聲明爲一個類友元。被聲明有關的函數或類能夠自由訪問受權類的任何私有成員。友元聲明能夠位於受權類的公有/私有/保護任何區域,其效果都同樣。
2)賦值類:右操做數可爲左值或右值,但左操做數必須是左值,表達式的值是左值且爲左操做數自己(而非副本)。
3)輸入輸出:左操做數是ostream/istream,右操做數對於輸出能夠是左值也能夠是右值,對於輸入只能是左值,表達式的值是左操做數自己。
Complex c1 (...);
Complex const c2 (...);
cout << c1; // cout.operator<< (c1)
// ::operator<< (cout, c1)
cout << c2;
cin >> c1;
cin >> c2; // ERROR
cout << c1 << c2 << endl;
4.單目操做符
#O/O#
->O.operator# ()
->::operator# (O)
1)運算類: 操做數既能夠是左值也能夠是右值,操做數在運算先後不發生變化,表達式的值是右值。
-x = 10; // ERROR
(0-x) = 10; // ERROR
2)前綴類:操做數爲左值,表達式的值是左值,並且就是操做數自己。運算先後操做數的值會發生變化,表達式的值是變化之後的值。
3)後綴類:操做數爲左值,表達式的值是右值,而是操做數運算以前的歷史備份。運算先後操做數的值發生變化,表達式的值是變化之前的值。
5.三目操做符:沒法重載
6.其它操做符:下標、函數、類型轉換
7.不能重載的操做符
::/./.*/?:/sizeof/typeid
8.操做符重載的限制
1)全部操做數都是基本類型的不能重載
int a = 1, b = 1;
int c = a + b; // c = 10000 ?
2)沒法改變操做符的優先級
z + x ^ y -> (z + x) ^ y
3)沒法改變操做符的目數
4)沒法發明新的操做符
**4
s1 @ s2
5)保持操做符的語義一致性
c1 + c2
str1 + str2
1、繼承的基本概念
1.共性和個性
學生:姓名、年齡、學號,吃飯、睡覺、學習
教師:姓名、年齡、工資,吃飯、睡覺、授課
---------------------------------------------
人類:姓名、年齡,吃飯、睡覺 - 共性
學生是人:學號,學習 - 個性
教師是人:工資,授課 - 個性
1)共性表達了不一樣類型事物之間共有的屬性和行爲。
2)個性則着意刻畫每種類型事物特有的屬性和行爲。
2.超集與子集
1)超集體現了基於共性的通常。
2)子集體現了針對個性的特殊。
3.基(父)類和子類
1)基類表示超集,體現共性,描述共有的屬性和行爲。
2)子類表示子集,體現個性,描述特有的屬性和行爲。
4.繼承與派生
基類: 人類(姓名、年齡、吃飯、睡覺)
派生 V / \ ^ 繼承
子類: 學生 教師
(學號、學習) (工資、授課)
5.繼承語法
class 子類 : 繼承方式1 基類1, 繼承方式2 基類2, ... {};
class Human {...}; // 人類
class Student : public Human {...}; // 學生
class Teacher : public Human {...}; // 教師
class Assistant : public Student, public Teacher {...}; // 助教
繼承方式:
public - 公有繼承,最多見的繼承方式
protected - 保護繼承
private - 私有繼承
6.公有繼承的特色
1)皆然性:子類即基類
學生是人
教師是人
任何子類對象中都包含着它的基類子對象,並且一般位於子類對象的低地址部分。所以把一個指向子類對象的指針或者引用子類對象的引用轉換爲其基類類型能夠隱式完成。實際上指這種轉換就意味着把一個子類對象的基類部分看做是一個實際的基類對象。這種類型轉換亦稱向上造型。
2)可訪問性
子類能夠訪問基類中哪些成員:公有成員、保護成員。基類的私有成員不能爲子類所直接訪問,可是在子類中存在。
3)隱藏性
若是在子類中定義和基類同名的標識符,那麼子類中標識符就會隱藏基類中的標識符。除非經過做用域限定符顯式地指明所訪問的標識符源自基類。
4)傳導性
當子類對象被構造、析構或者複製時,其基類子對象也須要同時被構造、析構或者複製。
A->B->C
當經過delete操做一個指向子類對象的基類指針時,實際被執行的是基類的析構函數,該函數不會調用(傳導)子類的析構函數,子類所特有動態資源將造成內存泄漏。
7.繼承方式對訪控屬性的影響
考慮繼承方式:經過子類訪問其所繼承。
class A { void foo (void) { ... } };
class B : public A {
void bar (void) {
foo(); //直接訪問基類,不考慮繼承方式
}
};
class C : public B {
void bar (void) {
foo (); //經過子類B訪問其從基類A中
//繼承的foo,須要考慮繼承方式
}
};
int main (void) {
B b;
b.foo (); // 經過B訪問其繼承的foo
// 須要考慮繼承方式
return 0;
}
基類 公子 保子 私子
公有 公有 保護 私有 //公有繼承保留基類訪控屬性
保護 保護 保護 私有 //保護繼承覆蓋基類公有屬性
私有 私有 私有 私有 //私有繼承覆蓋基類公有和保護屬性
8.私有繼承和保護繼承
class DCT { // $1000
public:
void codec (void);
};
class Jpeg : protected DCT {
public:
void render (void) {
... codec () ...
}
};
class Jpeg2K : public Jpeg {
};
// $1
class PDF : public Jpeg {
public:
void show (void) {
... render () ...
... codec () ... // ERROR
}
};
私有繼承亦稱實現繼承,其目的在於將基類中的公有和保護成員,在子類中私有化,防止其經過子類擴散。可是,若是但願在子類的子類能夠繼續訪問這些成員,而只是限制在子類外部的訪問,則能夠將子類從基類的繼承方式設置爲保護繼承。
保護和私有繼承不具備皆然性。
2、多重繼承
1.一個子類從多個基類中派生。
電話 媒體播放器 計算機
\ | /
智能手機
2.鑽石繼承問題
A
/ \
B C
\ /
D
A
/ | \
B C D
\ / \ /
E F
A
/ \
B D
| |
C |
\ /
E
1)一個子類繼承自多個基類,而這些基類又源自共同的祖先(公共基類),這樣的繼承結構稱爲鑽石繼承。
2)派生多箇中間子類的公共基類子對象,在繼承自多箇中間子類的匯聚子類對象中,存在多個實例。
3)在匯聚子類中,或經過匯聚子類對象,訪問公共基類的成員,會因繼承路徑的不一樣而致使不一致。這種現象稱爲鑽石繼承問題。
3.虛繼承
1)經過虛繼承,能夠保證公共基類子對象在匯聚子類對象中,僅存一份實例,且爲多箇中間子類對象所共享。
2)爲了表示虛繼承,須要在繼承表中使用virtual關鍵字。
3)通常而言,子類的構造函數不能指定間接基類的構造方式,可是一旦這個間接基類被聲明爲虛基類,它的全部子類,不管是直接子類仍是間接子類,都必須顯式地指明該公共基類子對象的構造方式,不然系統將按照缺省方式構造該子對象。
1、類型的決定性
經過一個指針或者引用訪問類的成員,編譯器只是根據指針或者引用的類型決定是否能夠訪問該成員,而與此指針或者引用的實際目標對象無關。
2、虛函數與多態
若是將基類中的某個成員函數聲明爲虛函數(在其返回類型前面加上virtual關鍵字),那麼其子類中的同型函數就也是虛函數(不管其是否帶有virtual關鍵字),並且和基類版本造成覆蓋關係。這時經過一個指向子類對象的基類指針,或者引用子類對象的基類引用,調用該虛函數,實際被執行的將是子類中的覆蓋版本,而非基類的原始版本。這種現象謂之多態。
3、重載、隱藏和覆蓋
重載必須在同一個做用域中。
覆蓋必須是同型的虛函數。
若是不是重載也不是覆蓋,並且函數名還同樣,那就必定是隱藏。
4、有效覆蓋的前提條件
1.只有類的非靜態成員函數才能被聲明爲虛函數,全局函數和類的靜態成員函數都不能是虛函數。
2.只有在基類中被聲明爲虛函數的成員函數才能在子類中覆蓋。
3.虛函數在子類中的覆蓋版本必須和該函數的基類版本擁有徹底相同的簽名,即函數名、形參表、常屬性嚴格一致。
4.若是基類中虛函數的返回類型爲基本類型或類類型的對象,那麼子類的覆蓋版本必須返回相同的類型。
5.若是基類中的虛函數返回類類型的指針或引用,那麼該函數在子類中的覆蓋版本能夠返回其基類版本返回類型的公有子類的指針或引用——類型協變。
class ShapeEditor
{
……
};
class Shape
{
public:
virtual const ShapeEditor& getEditor ()const = 0; //Factory Method
};
class Circle;
class CircleEditor : public ShapeEditor
{
……
};
class Circle : public Shape
{
public:
const CircleEditor& getEditor ()const ;
};
6.子類中覆蓋版本不能比基類版本說明拋出更多的異常。
7.不管基類中的虛函數位於該類的公有、私有仍是保護部分,該函數在子類中的覆蓋版本均可以出如今任何訪控區域。
class Base {
virtual void foo (void); // 1
virtual void foo (void) const; // 2
};
class Derived : public Base {
virtual void foo (void); // 3
virtual char foo (void) const; // 4
};
1和2構成重載
3和4構成重載
3隱藏了2,覆蓋了1
4隱藏了1,在試圖覆蓋2時出錯
重載在同一個類中,覆蓋在虛繼承的子類實現中必須有相同的簽名和返回值,不然隱藏。
5、多態的條件
多態性除了須要在子類和基類間造成有效的虛函數覆蓋之外,還必須經過指針或者引用訪問該虛函數。
當基類的構造函數被子類的構造函數調用時,子類對象尚不能說是子類類型的,它只表現出基類類型的外觀和行爲。這時調用虛函數,只能被綁定到基類版本,沒有多態性。
當基類的析構函數被子類的析構函數調用時,子類對象已再也不是子類類型的了,它只表現出基類類型的外觀和行爲。這時調用虛函數,只能被綁定到基類版本,沒有多態性。
6、純虛函數、抽象類、純抽象類
1.形如virtual 返回類型 函數名 (形參表) [const] = 0;的虛函數成爲純虛函數。
2.至少包含一個純虛函數的類就叫抽象類,抽象類不能實例化爲對象。
3.若是一個抽象類的子類沒有覆蓋其基類中的所有純虛函數,那麼該子類就也是抽象類。
4.除了構造、析構和靜態成員函數之外的所有成員函數都爲純虛函數的抽象類就叫作純抽象類,亦稱接口類。
7、虛函數表和動態綁定
1.對於包含虛函數的類,編譯器會爲其生成一張虛函數表,即存放每一個虛函數地址的函數指針數組,簡稱虛表(vtbl),每一個虛函數對應一個虛函數表的索引號。
2.當編譯器看到經過指針或引用調用虛函數時,並不急於生成有關函數調用的指令,相反它會用一段代碼替代該調用語句,這段代碼在運行時執行,完成以下操做:
1)肯定調用者指針或引用的目標對象,並從中獲取到虛表指針;
2)根據所調用函數的索引號從虛表中提取相應的函數地址;
3)根據函數地址調用該虛函數。
這個過程由於是在運行時完成的,因此稱爲動態綁定。
3.動態綁定對性能的影響
1)虛函數表自己會增長內存空間的開銷;
2)虛函數調用的時間開銷會大於普通函數;
3)虛函數不能內聯。
建議只有在確實須要多態性的場合才使用虛函數,不然儘可能避免使用虛函數。
8、運行時類型信息(RTTI)
1.動態類型轉換(dynamic_cast)
動態類型轉換應用在具備多態繼承關係的父子類的指針或引用之間。在運行期間檢查轉換源的目標對象的類型與轉換目的類型是否一致,若是一致則返回實際的對象指針或引用,不然返回空指針或拋出異常。
2.typeid運算符
在運行期間動態獲取對象的類型信息。
9、虛析構函數
若是將基類的析構函數聲明爲虛函數,那麼子類的析構函數就也是虛函數,並且對基類版本構成覆蓋。這時delete一個指向子類對象的基類指針,實際被執行的是子類的析構函數,該函數在釋放完成子類特有的資源之後,會自動調用基類的析構函數,完成對基類資源的釋放,最終釋放掉全部的資源,沒有內存泄漏。
10、虛與非虛
1.能夠被聲明爲虛函數的函數
普通成員函數
成員函數形式的操做符函數
析構函數
2.不能被聲明爲虛函數的函數
靜態成員函數
全局函數形式的操做符函數
構造函數
全局函數
1、異常
1.拋出異常:throw 異常對象;
異常對象:基本類型的變量或者類類型的對象。
throw 100;
throw "內存分配失敗";
throw MemoryEerror ();
2.捕獲異常
try {
可能引起異常的操做;
}
catch (異常類型1& 異常對象引用) {
對異常類型1的處理;
}
catch (異常類型2& 異常對象引用) {
對異常類型2的處理;
}
...
catch (...) {
對其它異常的處理;
}
3.異常流程
1)當代碼執行到throw語句時,一方面會將所拋出的異常對象複製到系統安全區中,另外一方面將流程執行到包含此throw語句的最小做用域的右花括號處,並沿着函數調用的路徑,向上回溯,直到try塊的右花括號處。而後根據異常對象的類型匹配一個適當的catch分支,執行其中的異常處理代碼。
2)若是一個被拋出的異常沒有被任何代碼捕獲,最終將被系統捕獲,並終止進程,同時打印異常的類型信息。
3)若是一個函數沒有捕獲它所引起的異常,那麼該異常將繼續向上拋出。直到調用路徑中的某個函數捕獲了該異常。
4.異常說明
throw (異常類型1, 異常類型2, ...)
1)在聲明函數時,能夠經過異常說明,聲明該函數所可能拋出的異常。該函數能夠拋出異常說明之外的異常,但這些異常不能被其調用者捕獲。
5.構造函數中的異常
1)在構造函數中能夠拋出異常,表示在構造過程當中出現的錯誤。
2)若是一個對象在構造過程當中出現了異常,那麼這個對象就會被不完整構造,而一個不完整構造的對象,它的析構函數永遠不會被執行。
6.析構函數中的異常
析構函數中不要拋出異常,對能夠引起異常的操做,儘可能在內部捕獲,不要使之被拋到析構函數外部。
7.標準庫異常
#include <stdexcept>
exception : const char* what (void) const throw () = 0;
--> overflow_error - 上溢異常
--> underflow_error - 下溢異常
--> invalid_argument - 無效參數異常
--> out_of_range - 越界異常
--> bad_alloc - 內存分配失敗(new)
--> bad_cast - 動態類型轉換失敗(引用)
--> bad_type_id - 非法類型
2、I/O流
ifstream - 輸入文件流,從文件讀取
ofstream - 輸出文件流,向文件寫入
fstream - 輸入輸出文件流,既可讀也可寫
打開->讀/寫/跳->關閉
operator bool (void) {}
istream& istream::read (char* buffer, streamsize num);
istream->bool : true - 讀到num字節
false - 讀取的字節數少於
num,或者出錯
istream::gcount ()返回最近一次讀取的字節數
ostream& ostream::write (
const char* buffer, streamsize num);
ostream->bool : true - 寫成功
false - 寫失敗
istream::seekg (off_type offset,
ios::seekdir origin);
ostream::seekp (off_type offset,
ios::seekdir origin);
origin - ios::beg
ios::cur
ios::end
pos_type istream::tellg (void);
pos_type osteam::tellp (void);
STL
C/C++
靜態類型
int i = 0;
char c = (char)i;
Student student ("張飛", 25);
cout << student.m_name << endl;
student.learn ("C++");
優勢:效率高、安全。
缺點:靈活性差、通用性差。
i = 100;
i = "hello";
i = Student (...);
1、模板的由來
1.C/C++語言的靜態類型系統在保證效率和安全性的同時,也增長了編寫與類型無關的通用代碼(數據結構和算法)的難度。
2.經過宏定義能夠擺脫類型的約束,但同時也失去了源自類型系統的安全性保障。
3.經過宏定義實現通常化的算法表示,利用預編譯器根據該宏生成針對不一樣類型的具體實現。
4.將預處理器生成具體函數的工做轉移到編譯器中——這就是模板的意義。
2、函數模板
1.定義
template<typename 模板形參1, typename 模板形參2, ...>
返回類型 函數模板名 (調用形參表) { ... }
例如:
template<typename A, typename B, typename C> A foo (B b) { C c; ...; }
2.使用
函數模板名<模板實參1, 模板實參2, ...> (調用實參表);
例如:
int a;
double b = 3.14;
a = foo<int, double, string> (b);
3.只有那些能夠知足函數模板所有要求的類型才能用於實例化該函數模板。
4.二次編譯
每一個函數模板事實上都要被編譯兩次。一次是在實例化以前,先檢查模板代碼有無語法硬傷,若是檢查經過則生成該函數模板的內部表示。另外一次是在實例化該函數模板的過程當中,即將函數模板變成具體函數的過程,結合所提供的模板實參再次檢查模板代碼的正確性,若是檢查經過則生成具體函數的二進制指令。
5.隱式推斷
若是函數模板調用參數的類型與該模板的模板參數相關,那麼在調用該函數模板時即便不顯式指定模板實參,編譯器也有能力根據調用實參的類型隱式地推斷出相應模板參數的類型。
6.函數模板重載
1)函數模板和普通函數同樣,也能夠重載,重載解析的具體規則見代碼overload.cpp。
2)在重載函數模板的時候,應該儘量地把改變限制在參數的個數或參數的具體類型上,而對於參數的引用或常屬性儘可能保持不變,以免出現返回局部對象引用的狀況。
3、類模板
1.類的成員變量、成員函數和基類若是包含參數化的類型,那麼這個類就稱爲類模板。
2.定義
template<typename 模板形參1, typename 模板形參2, ...>class 類模板名 { ... };
3.使用
類模板自己並非一個類型,只有先將類模板實例化爲具體類,才能用於建立對象,聲明變量。
類模板名<模板實參1, 模板實參2, ...>
類模板不能隱式推斷,必須顯式實例化。
4.類模板的兩步實例化
實例化 實例化
類模板------>類------>對象
編譯期 運行期
編譯器 處理器
5.類模板全部的成員函數,包括構造函數、析構函數、運算符函數、通常成員函數、靜態成員函數,無一例外地都是函數模板。
6.在類模板聲明中,凡是使用類型的場合,嚴格的語法應該是:"類模板名<模板參數>";而對類名的引用則能夠直接使用"類模板名"。
7.類模板中,只有那些被調用的成員函數纔會被實例化,即產生二進制指令代碼。所以某些類型雖然並無提供類模板所須要的所有功能,但照樣能夠實例化該類模板,只要不直接或間接調用那些依賴於未提供功能的成員函數便可。
8.類模板的靜態成員變量,在該模板的每一個實例化類中各有一份獨立的拷貝,併爲該實例化類的全部對象所共享。
類模板->類1->對象1
->對象2
->類2->對象3
->對象4
9.類模板遞歸實例化
用類模板的實例化類實例化該類模板自身。
一般用這種方法表達一些在空間上具備遞歸特性的數據結構,如多維數組、複合鏈表、多叉樹等。
3、類模板
9...
10.特化
1)一個針對任意類型的通用的類模板可能對於某些個別的特殊類型並不可以很好地支持,這時能夠經過特化定義一個專門針對某個特定類型的實現取代通用版本,這就叫作類模板的特化(特例化)。
2)全類特化:對整個類模板進行特化。
template<>
class 類模板名<特化實參> { ... };
例如:
template<>
class Comparator<char const*> { ... };
3)成員特化:只特化類模板中那些與類型相關的成員函數。
template<>
返回類型 類模板名<特化實參>::成員函數名 (
調用形參表) [const] { ... }
例如:
template<>
char const* Comparator<
char const*>::max (void) const { ... }
4)全類特化至關於另寫了一個新的類,其實現能夠和通用版本徹底不一樣,而成員特化,其聲明與通用版本共享,其函數原型除了把通用版本的類型參數變成具體的特化類型之外,不能有任何不一樣。
11.局部特化(偏特化)
1)類模板能夠被局部特化,即一方面爲類模板指定特定的實現,另外一個方面又容許用戶對部分模板參數自行指定。
2)若是多個局部特化同等程度地匹配某個聲明,那麼該聲明將因二義性而致使歧義錯誤。
3)函數模板不支持局部特化。
12.缺省參數
1)類模板的模板參數能夠帶有缺省值,即缺省模板參數。
2)若是某個類模板的模板參數帶有了缺省值,那麼它後面的全部模板參數必須都帶有缺省值。
3)C++98標準規定函數模板不能帶有缺省模板參數,可是C++2011標準容許函數模板帶有缺省模板參數。
GCC4.6:g++ ... -std=c++0x
GCC4.8:g++ ... -std=c++11
4)模板參數的缺省值能夠取前面定義的參數。
13.非類型參數
1)不管是函數模板仍是類模板均可以帶有數值形式的參數,即非類型參數。
2)類模板和C++2011之後的函數模板的非類型參數均可以帶有缺省值。
3)傳遞給模板非類型參數的實參只能是常量、常量表達式、帶有常屬性(const)的變量,可是不能同時具備揮發性(volatile)。
4)模板的非類型參數必須是整數型,具體包括char/short/int/long/long long及其unsigned版本,還包括指針。
5)向模板傳遞字符串:須要用具備外部連接特性的字符數組做爲非類型實參。
字面值字符串不能夠。
string對象不能夠。
全局指針不能夠。
只有內部連接特性的字符數組不能夠。
4、其它的技術細節
1.嵌套依賴
若是須要使用某個依賴於模板參數的內部類型,那麼就須要在該類型前面加上typename關鍵字,不然編譯器會將該內部類型解釋爲一個靜態成員變量,進而致使編譯錯誤。
class - 聲明類
\ 聲明模板的
/ 類型參數
typename - 解決嵌套依賴
2.依賴於模板參數的模板成員
若是須要調用模板參數類型中的模板型成員函數,那麼須要在該成員函數名前面加上template關鍵字,不然該函數會被編譯器解釋爲一個普通函數,而對後面的"<>"產生編譯錯誤。
3.在子模板中訪問基模板
在子模板中訪問那些在基模板中聲明且依賴於模板參數的符號,應該在它們前面加上做用域限定符「基模板::」或this指針「this->」,不然編譯器將只在子類和全局做用域中查找所訪問的符號,而忽略基類的做用域。
4.模板形式的模板參數
5.零初始化
T t = T (); - string t = string (); //缺省構造
- int t = int (); // 整數0
- double t = double (); // 浮點0
6.類模板中能夠定義模板成員函數,類模板中也能夠定義虛函數,可是類模板中的虛函數不能同時又是模板成員函數。
解釋:類模板的模板型成員函數不能是虛函數,即不能爲類模板定義虛成員函數模板。虛函數調用機制的廣泛實現須要依賴於一個名爲「虛函數表」的函數指針數組。該數組在類模板被實例化的過程當中生成,而此時成員函數模板還沒有實例化,其入口地址和重載版本的個數,要等到編譯器處理完對該函數的全部調用之後才能肯定。成員函數模板的延遲編譯阻礙了虛函數表的靜態構建。
7.包含模型
A類 - a.h - 聲明A類
- a.cpp - 實現A類
- main.cpp - 使用A類
1)優勢:把模板的聲明、實現和使用經過文件包含的方式約束到同一個編譯單元中,使編譯器看到模板被實例化的同時也能找到模板的定義,避免連接錯誤。
2)缺點:模板的實現源代碼沒法對用戶保密。延長編譯時間。
max.cpp // 3
|
max.h
/ | \
a.cpp b.cpp c.cpp
經過預編譯頭能夠在必定程度上提高編譯速度。
容器、迭代器和泛型算法
1、容器
藉助模板語法實現通用的數據結構。
2、泛型算法
以通常化的方式訪問或處理容器中的數據。
3、迭代器
以一種一致且透明的方式訪問任意容器中的數據。
以雙向線性鏈表容器爲例。
STL - Standard Templates Library,標準模板庫
三大組件:容器、迭代器和泛型算法。
1、十大容器
1.線性容器
1)向量(vector)
2)雙端隊列(deque)
3)列表(list)
2.適配器容器
1)堆棧(stack)
2)隊列(queue)
3)優先隊列(priority_queue)
3.關聯容器
1)映射(map)
2)多重映射(multimap)
3)集合(set)
4)多重集合(multiset)
1、向量
1.基本特性
1)連續存儲與下標訪問
2)動態內存分配
3)經過預分配內存空間下降動態內存管理的開銷
4)總體性複製,支持深拷貝
int a[5] = {10, 20, 30, 40, 50};
int b[5];
// b = a; // 錯誤
for (int i = 0; i < 5; ++i)
b[i] = a[i];
memcpy (b, a, 5 * sizeof (int));
5)支持在任意位置的插入和刪除,可是效率並不平均,插入刪除的位置越靠近容器的尾端,容器中的元素個數越少,操做效率越高,反之越低。
2.實例化
#include <vector>
using namespace std;
1)建立空向量
vector<元素類型> 向量對象;
vector<int> vi;
2)指定初始大小
vector<元素類型> 向量對象 (元素個數);
vector<int> vi (5);
基本類型:用0初始化全部的元素。
類類型:用缺省構造函數初始化全部的元素。
3)指定初始大小同時指定初值
vector<元素類型> 向量對象 (元素個數,
元素初值);
vector<int> vi (5, 13);
vector<Student> vs (5,
Student ("張飛", 22));
4)經過一個已有的容器初始化
vector<元素類型> 向量對象 (起始迭代器,
終止迭代器);
int a[5] = {1, 2, 3, 4, 5};
vector<int> vi (a, a + 5);
vector<int> vi (&a[0], &a[5]);
vector<int> vi (&a[0], &a[4]); // 錯誤
3.元素訪問
1)下標運算符:向量對象[基0索引];
2)迭代器
iterator - 正向迭代器
const_iterator - 常正向迭代器
reverse_iterator - 反向迭代器
const_reverse_iterator - 常反向迭代器
隨機迭代器,連續內存容器的固有特徵
能夠整數作加減法運算
迭代器之間能夠作"<"或">"比較運算
迭代器之間能夠作減法運算
注意,任何致使容器結構發生變化操做均可能引發內存佈局的改變,這時先前獲取到的迭代器可能失效,須要從新獲取一次再繼續使用。
4.其它成員函數
push_back()
pop_back()
back()
front()
erase()
insert()
begin()/end()
rbegin()/rend()
5.泛型算法
#include <algorithm>
iterator find (iterator begin,
iterator end, value_type const& key);
查找成功返回第一個匹配元素的迭代器,失敗返回end。
void sort (iterator begin, iterator end);
void sort (iterator begin, iterator end,
less cmp);
將[begin, end)範圍中元素按升序排列。
6.類類型向量
元素類型須要支持深拷貝。
可能還須要支持缺省構造。
可能還須要支持小於運算或者小於比較器。
7.大小和容量
大小:實際容納元素的個數。
容量:最多容納元素的個數。
size() - 大小
capacity() - 容量
class Student {
char m_name[1024];
};
class Student {
Student (char* const name) :
m_name (new char[1024]) {
strcpy (m_name, name);
}
~Student {
delete[] m_name;
}
char* m_name;
};
class Student {
string m_name;
};
2、雙端隊列
物理結構和向量同樣,也是用連續的內存空間保存數據元素,惟一區別就是前端開放,體如今多了兩個成員函數:push_front/pop_front
其它方面與向量徹底一致。
3、列表
1.優勢:空間利用率高,隨機插入刪除效率高
2.缺點:隨機訪問效率低下
3.經常使用成員函數
front/push_front/pop_front
back/push_back/pop_back
insert/erase
size/clear/empty
begin/end/rbegin/rend
remove
將連續重複的元素惟一化
void unique (void);
排序
void sort (void);
拆分
void splice (iterator pos, list& ls);
將ls中的所有元素剪切到調用列表的pos以前
void splice (iterator pos, list& ls,iterator del);
將ls中的del元素剪切到調用列表的pos以前
void splice (iterator pos, list& ls,iterator begin, iterator end);
將ls中從begin到end之間的元素剪切到調用列表的pos以前
合併
void merge (list& ls);
將有序的ls合併到有序的調用列表中,保證合併後的結果依然有序。
4、堆棧
1.實例化
#include <stack>
stack<元素類型[, 底層容器類型]> 堆棧對象;
2.底層容器:vector/deque(缺省)/list
3.成員函數
push -> push_back
pop -> pop_back
top -> back
size -> size
empty -> empty
5、隊列
1.實例化
#include <queue>
queue<元素類型[,底層容器類型]> 隊列對象;
2.底層容器:deque(缺省)/list
3.成員函數
push -> push_back
pop -> pop_front
back -> back
front -> front
size -> size
empty -> empty
6、優先隊列
1.實例化
#include <queue>
priority_queue<
元素類型[,底層容器類型[,比較器類型]]>
優先隊列對象;
2.底層容器:vector/deque(缺省)
3.成員函數
push
pop - 彈出優先級最高的元素
top - 獲取優先級最高的元素
size
empty
4.優先級
1)缺省狀況,經過元素類型「<」運算符比較大小,以大者爲優。
2)經過比較器人爲規定元素的優先級
7、映射
1.經過平衡有序二叉樹存放由鍵和值組成的一一對應序列。
2.鍵必須是惟一的。
3.經過pair容器封裝鍵和值。
template<typename F, typename S>
class pair {
public:
pair (F const& f, S const& s) :
first (f), second (s) {}
F first;
S second;
};
用pair::first表示鍵,用pair::second表示值。:q