筆記呢只簡要記錄了老師講課的關(guān)鍵知識點和結(jié)構(gòu),然后積累了一些有用的代碼小片段和解釋性很強的例子霸妹,之所以沒有分章節(jié)是為了使用瀏覽器頁內(nèi)查找(ctrl+f/ command+f)時方便十电。
課程的編程作業(yè)的代碼我也上傳了gayhub(在這里呀https://github.com/BMR731/XueTangCplusplus).
有錯誤的地方希望大家指出啦,有一起繼續(xù)學習C++的小伙伴也可以一起交流哦~
- auto 變量類型叹螟,如
auto i = j+k;
vector<int> v(5);
for(auto e : v){
cout<<e;
}
- 控制臺傳參數(shù)
int main(int argc, char* argv[]){
//argc是參數(shù)的個數(shù)鹃骂,包括程序執(zhí)行本身的一個參數(shù);argv是一個字符串的數(shù)組
}
- decltype 的使用罢绽,
decltype(i) j = 2;//聲明一個與i同類型的j
- C++風格的安全類型轉(zhuǎn)換畏线,如
int j = static_cast<int>(i);
- dynamic_cast的轉(zhuǎn)換,dynamic_cast主要用于類層次間的上行轉(zhuǎn)換和下行轉(zhuǎn)換良价,還可以用于類之間的交叉轉(zhuǎn)換寝殴。在類層次間進行上行轉(zhuǎn)換時,dynamic_cast和[static_cast]的效果是一樣的明垢;
在進行下行轉(zhuǎn)換時橄登,dynamic_cast具有類型檢查的功能痛悯,比static_cast更安全鸵赫。
下面是類型轉(zhuǎn)換時有用的代碼片段:
if(Derived* dp= dynamic_cast<Derived*)(bp)){
//轉(zhuǎn)換成功航邢,dp指向Derived對象
}else{
//轉(zhuǎn)換失敗,bp指向Base對象,
}
//把轉(zhuǎn)換語句寫在條件判斷中更加安全
- char to int 的簡單轉(zhuǎn)換
int char2int(char c){
return static_cast<int>(c) - 48;
}
- 保留小數(shù)點后兩位
float f = 1.234;
cout<<fixed<<setprecision(2)<<f<<endl;
- 反轉(zhuǎn)一個正整數(shù)的代碼段
unsigned i = n;//n is the number we try to reverse
int m=0;//m is the auxiliary number
while(i>0){
m= m*10 + i%10;
i = i/10;
}
//m is the reversed n;
- 隨機數(shù)的使用
#include <cstdlib>
cin>>seed;
srand(seed);
int i = rand()%6 + 1;//模擬扔骰子
- 【todo】可變參數(shù)傳遞問題
- 內(nèi)聯(lián)函數(shù): 聲明時使用關(guān)鍵字 inline贞绳。編譯時在調(diào)用處用函數(shù)體進行替換谷醉,節(jié)省了參數(shù)傳遞、控制轉(zhuǎn)移等開銷冈闭。
注意:
內(nèi)聯(lián)函數(shù)體內(nèi)不能有循環(huán)語句和switch語句俱尼;
內(nèi)聯(lián)函數(shù)的定義必須出現(xiàn)在內(nèi)聯(lián)函數(shù)第一次被調(diào)用之前;
對內(nèi)聯(lián)函數(shù)不能進行異常接口聲明萎攒。
inline double calArea(int r){//計算圓面積
return PI*r*r;
}
- 傳遞引用有兩個優(yōu)點:
- 為了實現(xiàn)雙向修改
- 為了節(jié)省傳遞的開銷号显,但又不允許修改源數(shù)據(jù),如復制構(gòu)造函數(shù)
foo(const class &A){...}
,添加const關(guān)鍵詞即可
- constexpr函數(shù):constexpr修飾的函數(shù)在其所有參數(shù)都是constexpr時躺酒,一定返回constexpr;并且只有一條return語句蔑歌;好處是表達式的值可以在編譯時進行確定羹应。
constexpr int get_size(){ return 20;}
constexpr int foo = get_size();//此時可以確保foo是一個常量表達式
- 默認參數(shù)值:
int add(int x, int y=2, int z=3);//對的,默認參數(shù)給的順序必須從右到左
int add(int x =1, int y, int z=3);//錯
- 函數(shù)重載:注意一下次屠,返回值和形參類型不能區(qū)分重載函數(shù)即可
- 在自定義構(gòu)造函數(shù)后仍希望編譯器給出默認參數(shù)的話,可這樣寫
clock() = default;
- 用初始化列表的方式賦值更快
Clock::Clock(int newH,int newM,int newS): hour(newH),minute(newM), second(newS) {
}
- 設計類時园匹,寫一個的默認構(gòu)造函數(shù)是良好的設計規(guī)范。
- 委托構(gòu)造函數(shù)就是可以在構(gòu)造函數(shù)中調(diào)用其他構(gòu)造函數(shù)的機制
- 復制構(gòu)造函數(shù)
class 類名 {
public :
類名(形參)劫灶;//構(gòu)造函數(shù)
類名(const 類名 &對象名)裸违;//復制構(gòu)造函數(shù)
// ...
};
類名::類( const 類名 &對象名)//復制構(gòu)造函數(shù)的實現(xiàn)
{ 函數(shù)體 }
//如果不希望被復制
//class Point { //Point 類的定義
public:
Point(int xx=0, int yy=0) { x = xx; y = yy; } //構(gòu)造函數(shù)本昏,內(nèi)聯(lián)
Point(const Point& p) =delete; //指示編譯器不生成默認復制構(gòu)造函數(shù)
private:
int x, y; //私有數(shù)據(jù)
};
//復制構(gòu)造函數(shù)被調(diào)用的三種時機
//1 用一個對象初始化對象時
//2 形參和實參結(jié)合時
//3 return 語句返回一個無名對象時
- 析構(gòu)函數(shù)
完成對象被刪除前的一些清理工作供汛。
在對象的生存期結(jié)束的時刻系統(tǒng)自動調(diào)用它,然后再釋放此對象所屬的空間涌穆。
如果程序中未聲明析構(gòu)函數(shù)怔昨,編譯器將自動產(chǎn)生一個默認的析構(gòu)函數(shù),其函數(shù)體為空宿稀。
class Point {
public:
Point(int xx,int yy);
~Point();
//...其他函數(shù)原型
private:
int x, y;
};
- 前向引用申明:為了解決兩個類在定義時相互引用的情況趁舀,但又不能完美解決所有情況,如它可以解決充當形參的情況祝沸,但不能解決充當成員變量的情況矮烹,因為涉及到具體的字節(jié)數(shù)等細節(jié)問題。
class B; //前向引用聲明
class A {
public:
void f(B b);
};
class B {
public:
void g(A a);
};
- 類的靜態(tài)成員別忘了在類外進行定義和初始化
class foo{
private:
static int count;
}
int foo::count =0;//this line don't forget;在類外進行定義和初始化罩锐!
int main(){}
- 結(jié)構(gòu)體;結(jié)構(gòu)體是一種特殊形態(tài)的類
與類的唯一區(qū)別:類的缺省訪問權(quán)限是private奉狈,結(jié)構(gòu)體的缺省訪問權(quán)限是public
結(jié)構(gòu)體存在的主要原因:與C語言保持兼容
什么時候用結(jié)構(gòu)體而不用類呢?定義主要用來保存數(shù)據(jù)、而沒有什么操作的類型唯欣;人們習慣將結(jié)構(gòu)體的數(shù)據(jù)成員設為公有嘹吨,因此這時用結(jié)構(gòu)體更方便 - 聯(lián)合體:目的是存儲空間的共用,減少冗余和錯誤境氢。
- 枚舉類:實質(zhì)上就是強類型的枚舉蟀拷,與簡單枚舉相比碰纬,防止沖突;類型要求嚴格问芬;更加多樣的基本類型悦析,
enum class Type: char { General, Light, Medium, Heavy};
- 類的友元:友元機制是破壞封裝的一種機制,為的是提供封裝和效率的折中此衅,在水平不高時最好少使用强戴;友元是一種單向的關(guān)系;
//友元函數(shù)
class Point { //Point類聲明
public: //外部接口
Point(int x=0, int y=0) : x(x), y(y) { }
int getX() { return x; }
int getY() { return y; }
friend float dist(Point &a, Point &b);
private: //私有數(shù)據(jù)成員
int x, y;
};
float dist( Point& a, Point& b) {
double x = a.x - b.x;
double y = a.y - b.y;
return static_cast<float>(sqrt(x * x + y * y));
}
//友元類
class A {
friend class B;
public:
void display() {
cout << x << endl;
}
private:
int x;
};
class B {
public:
void set(int i);
void display();
private:
A a;
};
void B::set(int i) {
a.x=i;
}
void B::display() {
a.display();
};
- const的用法
- 常函數(shù)
void A::print() const;
對于保證不改變對象狀態(tài)的函數(shù)挡鞍,優(yōu)先聲明const來得到編譯器的保證檢查骑歹。 - 常變量
const int a = 2;
- 常引用:用于傳引用但保證單向傳遞的情況,
void foo(const int &a)
- 常函數(shù)
- 多文件結(jié)構(gòu)
- .h文件:類的聲明
- .cpp文件: 類的實現(xiàn)
- main()所在文件:類的使用文件
- 預編譯指令:
#include
#define
#if....#endif 條件編譯
#if..#elif....#else...#endif
#ifdef.. #endif 如果標記被定義過
#ifndef....#endif 如果標記未被定義過
最常用的用法在類的聲明文件中墨微,為了避免重復編譯道媚,常這樣寫:
#ifndef CLIENT_H
#define CLIENT_H
...類的聲明
#endif
- 指針相關(guān)
const int* p = &i; //表明p為只讀指針,但指針本身可以指向其他地方
int* const p = &i;//表明指針本身只能指向i的地址翘县,但可對i進行讀寫操作
//void指針作為通用指針來使用
void* p;
int i=0;
p = &i;
int* p2 = static_cast<int*>(p);
//空指針最域;
int* p = nullptr; //c++11 推薦
p==0;//判斷指針是否為空
指針做函數(shù)參數(shù),為什么要用指針
需要數(shù)據(jù)雙向傳遞時(引用也可以達到此效果)
需要傳遞一組數(shù)據(jù)锈麸,只傳首地址運行效率比較高返回指針類型的函數(shù)镀脂,1.特別注意返回的地址不能是局部變量的地址,必須在主調(diào)函數(shù)中有效忘伞。2.函數(shù)返回用new分配的空間分配的地址是可以的薄翅,但主調(diào)函數(shù)必須記得釋放空間
函數(shù)指針。函數(shù)指針的主要用途是實現(xiàn)函數(shù)回調(diào)虑省,從而調(diào)用者可以將函數(shù)作為參數(shù)匿刮,更加靈活的處理數(shù)據(jù)。函數(shù)指針與其他類型的指針聲明相同探颈,只不過要求更多一些熟丸,需要表明函數(shù)的返回值,參數(shù)表伪节,如
int(*func)(int, int)
表明這是一個指向返回值類型為int光羞,參數(shù)表為(int,int)的函數(shù)的程序代碼的地址。
int compute(int a, int b, int(*func)(int,int)){
return func(a,b);
}
int max(int a, int b){return ((a>b)? a:b;)}
int min(int a, int b){return ((a<b)? a:b;)}
res = compute(a,b,&max);
res = compute(a,b,&min);
- 對象指針怀大,1. 了解
pa->getX()與(*pa).getX()
等價 2.了解this指針 - 動態(tài)分配內(nèi)存
//分配多維數(shù)組
int (*cp)[8][9] = new int[7][8][9];
左值和右值的問題纱兑,實質(zhì)上是能不能修改的問題,右值表示只可以讀但不可以修改化借,而左值才能修改潜慎,返回左值的常見做法是返回引用類型或指針類型。
智能指針[Todo有待補充理解]
unique_ptr :不允許多個指針共享資源,可以用標準庫中的move函數(shù)轉(zhuǎn)移指針
shared_ptr :多個指針共享資源
weak_ptr :可復制shared_ptr铐炫,但其構(gòu)造或者釋放對資源不產(chǎn)生影響深層復制和淺層復制
當類成員為指針變量時垒手,如一個數(shù)組,淺層復制只是復制了指針的值倒信,而深層復制才可以復制指針所指的內(nèi)容科贬,此時多需要重寫復制拷貝函數(shù)移動構(gòu)造函數(shù):在一些情況下,不需要復制構(gòu)造時鳖悠,而只需簡單移動榜掌,將控制權(quán)轉(zhuǎn)移給目標對象時,可使用移動構(gòu)造函數(shù)乘综,書寫格式為
class_name ( class_name && )
&&表示右值引用憎账。求字符串所有子序列
vector<string> get_subsequences(const string str){
long len = str.length();
long num = 1<<str.length();//將1左移len位,求2的len次冪卡辰。
vector<string> res;
for (int i = 1; i <num ; ++i) {
string ss;
for (int j = 0; j < len; ++j) {
if(i&(1<<j)) ss.push_back(str[j]);
}
res.push_back(ss);
}
return res;
}
- vector的使用
vector<int> nums(5,2);//初始化
sort(nums.begin(), nums.end());//排序
派生的繼承方式:
公有繼承(public)
繼承的訪問控制:
基類的public和protected成員:訪問屬性在派生類中保持不變鼠哥;
基類的private成員:不可直接訪問。
訪問權(quán)限:
派生類中的成員函數(shù):可以直接訪問基類中的public和protected成員看政,但不能直接訪問基類的private成員;
通過派生類的對象:只能訪問public成員抄罕。
私有繼承
繼承的訪問控制
基類的public和protected成員:都以private身份出現(xiàn)在派生類中允蚣;
基類的private成員:不可直接訪問。
訪問權(quán)限
派生類中的成員函數(shù):可以直接訪問基類中的public和protected成員呆贿,但不能直接訪問基類的private成員嚷兔;
通過派生類的對象:不能直接訪問從基類繼承的任何成員。
保護繼承(protected)
繼承的訪問控制
基類的public和protected成員:都以protected身份出現(xiàn)在派生類中做入;
基類的private成員:不可直接訪問冒晰。
訪問權(quán)限
派生類中的成員函數(shù):可以直接訪問基類中的public和protected成員,但不能直接訪問基類的private成員竟块;
通過派生類的對象:不能直接訪問從基類繼承的任何成員壶运。
protected 成員的特點與作用
對建立其所在類對象的模塊來說,它與 private 成員的性質(zhì)相同浪秘。
對于其派生類來說蒋情,它與 public 成員的性質(zhì)相同。
既實現(xiàn)了數(shù)據(jù)隱藏耸携,又方便繼承棵癣,實現(xiàn)代碼重用。
如果派生類有多個基類夺衍,也就是多繼承時狈谊,可以用不同的方式繼承每個基類。基類和私有類之間的類型轉(zhuǎn)換
公有派生類對象可以被當作基類的對象使用,反之則不可河劝。
派生類的對象可以隱含轉(zhuǎn)換為基類對象壁榕;
派生類的對象可以初始化基類的引用;
派生類的指針可以隱含轉(zhuǎn)換為基類的指針丧裁。
通過基類對象名护桦、指針只能使用從基類繼承的成員。派生類的構(gòu)造函數(shù):首先煎娇,按照繼承的次序二庵,確保給基類帶參數(shù)的初始化函數(shù)送去參數(shù),接著按照類成員的聲明次序初始化類成員缓呛,最后調(diào)用構(gòu)造函數(shù)催享。
class C: public B {
public:
C();
C(int i, int j);
~C();
void print() const;
private:
int c;
};
C::C(int i,int j): B(i), c(j){
cout << "C's constructor called." << endl;
}
派生類的復制構(gòu)造函數(shù)
一般都要為基類的復制構(gòu)造函數(shù)傳遞參數(shù)。
復制構(gòu)造函數(shù)只能接受一個參數(shù)哟绊,既用來初始化派生類定義的成員因妙,也將被傳遞給基類的復制構(gòu)造函數(shù)。
基類的復制構(gòu)造函數(shù)形參類型是基類對象的引用票髓,實參可以是派生類對象的引用
例如:C::C(const C &c1): B(c1) {…}
派生類的析構(gòu)函數(shù):無需顯示調(diào)用攀涵,析構(gòu)次序與初始化次序相反。
二義性:二義性可以發(fā)生在父子之間洽沟,父親之間以故,簡單的解決方案是使用類名加以限定即可,然而在多繼承下的二義性問題比較復雜裆操,因此引入了虛基類怒详,當多個父親有一個共同的祖先時,祖先里的成員存在不一致性和冗余的風險踪区,因此通過虛繼承來保證祖先中的值只有一份昆烁,并且祖先的構(gòu)造函數(shù)需要在每一代的構(gòu)造函數(shù)中傳參,但實質(zhì)上執(zhí)行的只有最遠派生類調(diào)用的構(gòu)造函數(shù)缎岗。
C++中class聲明的默認權(quán)限是private静尼,struct聲明的默認權(quán)限是public
重載運算符,重載函數(shù)可以重載為類內(nèi)成員函數(shù)传泊,或者類外函數(shù)茅郎。
重載為類內(nèi)成員函數(shù)的要求是,操作符的第一個參數(shù)必須是該類的類型或渤。
//雙目運算符的重載
//例8-1復數(shù)類加減法運算重載為成員函數(shù)
Complex Complex::operator + (const Complex &c2) const{
//創(chuàng)建一個臨時無名對象作為返回值
return Complex(real+c2.real, imag+c2.imag);
}
//單目運算符的重載
//重載前置++
Clock & Clock::operator ++ () {
second++;
if (second >= 60) {
second -= 60; minute++;
if (minute >= 60) {
minute -= 60; hour = (hour + 1) % 24;
}
}
return *this;//返回值的本身引用系冗,可以當左值被修改
}
//重載后置++
Clock Clock::operator ++ (int) {
//注意形參表中的整型參數(shù)
Clock old = *this;
++(*this); //調(diào)用前置“++”運算符
return old;//返回值的一個副本,只能做右值薪鹦,不能做觸及到本身的修改
}
重載為非成員函數(shù)的規(guī)則
函數(shù)的形參代表依自左至右次序排列的各操作數(shù)掌敬。
重載為非成員函數(shù)時惯豆,參數(shù)個數(shù)=原操作數(shù)個數(shù)(后置++、--除外)
至少應該有一個自定義類型的參數(shù)奔害。
后置單目運算符 ++和--的重載函數(shù)楷兽,形參列表中要增加一個int,但不必寫形參名华临。
如果在運算符的重載函數(shù)中需要操作某類對象的私有成員芯杀,可以將此函數(shù)聲明為該類的友元。
典型例題:
class Complex {
public:
Complex(double r = 0.0, double i = 0.0) : real(r), imag(i) { }
friend Complex operator+(const Complex &c1, const Complex &c2);//聲明類外的函數(shù)為友元來提高訪問的效率
friend Complex operator-(const Complex &c1, const Complex &c2);
friend ostream & operator<<(ostream &out, const Complex &c);
private:
double real; //復數(shù)實部
double imag; //復數(shù)虛部
};
Complex operator+(const Complex &c1, const Complex &c2){//注意這里傳const的引用來提高傳輸效率
return Complex(c1.real+c2.real, c1.imag+c2.imag);
}
Complex operator-(const Complex &c1, const Complex &c2){
return Complex(c1.real-c2.real, c1.imag-c2.imag);
}
ostream & operator<<(ostream &out, const Complex &c){
out << "(" << c.real << ", " << c.imag << ")";
return out;//返回為ostream的引用來繼續(xù)保持cout的級聯(lián)輸出
}
-
虛函數(shù):
- 虛函數(shù)是實現(xiàn)動態(tài)綁定的一種機制雅潭,告訴編譯器運行時綁定從而實現(xiàn)多態(tài)
- 虛函數(shù)同時不可以成為內(nèi)聯(lián)函數(shù)揭厚,必須在類外定義
- 基類只要是虛函數(shù),則它子類的相同函數(shù)也一定是虛函數(shù)扶供,不過我們也要顯示的聲明來增加可讀性
- 在繼承時筛圆,不要重寫父類的非虛函數(shù),否則靜態(tài)綁定后全都會使用父類的函數(shù)版本椿浓。
virtual void print() const
虛析構(gòu)函數(shù):沒有虛構(gòu)造函數(shù)太援,但是有虛析構(gòu)函數(shù),為什么需要虛析構(gòu)函數(shù)扳碍? 可能通過基類指針刪除派生類對象提岔; 如果你打算允許其他人通過基類指針調(diào)用對象的析構(gòu)函數(shù)(通過delete這樣做是正常的),就需要讓基類的析構(gòu)函數(shù)成為虛函數(shù)笋敞,否則執(zhí)行delete的結(jié)果是不確定的唧垦。
抽象類:多用作基類用于規(guī)范接口,只要有一個純虛函數(shù)的類就是抽象類液样,不能實例化,其中巧还,純虛函數(shù)語法為
virtual void print() const = 0;
override和final的使用鞭莽,override的使用可以使編譯器在編譯時進行檢查,以免發(fā)生難以調(diào)試的運行時錯誤麸祷,要習慣使用澎怒。
struct B4
{
virtual void g(int) {}
};
struct D4 : B4
{
virtual void g(int) override {} // OK
virtual void g(double) override {} // Error
};
struct B2
{
virtual void f() final {} // final 函數(shù)
};
struct D2 : B2
{
virtual void f() {}
};
- 注意區(qū)分虛基類和虛函數(shù)的作用,虛基類是為了消除多繼承中的二義性而引入的阶牍,而虛函數(shù)是為了實現(xiàn)多態(tài)性而引入的喷面。
- 模板:編譯器幫我們的一種機制,使用時注意若要操作自定義類型時走孽,請確保在類內(nèi)重載了相應的運算符惧辈。
//函數(shù)模板
template <typename T>
T add(T x, T y){
return x+y;
}
//類模板
template <class T>
class Foo{
T element;
Foo();
}
Foo<T>::Foo(){}//注意此時在類外標注類名時要把模板參數(shù)帶上,寫成Foo<T>::
術(shù)語:概念
用來界定具備一定功能的數(shù)據(jù)類型。例如:
將“可以比大小的所有數(shù)據(jù)類型(有比較運算符)”這一概念記為Comparable
將“具有公有的復制構(gòu)造函數(shù)并可以用‘=’賦值的數(shù)據(jù)類型”這一概念記為Assignable
將“可以比大小磕瓷、具有公有的復制構(gòu)造函數(shù)并可以用‘=’賦值的所有數(shù)據(jù)類型”這個概念記作Sortable
對于兩個不同的概念A和B盒齿,如果概念A所需求的所有功能也是概念B所需求的功能念逞,那么就說概念B是概念A的子概念。例如:
Sortable既是Comparable的子概念边翁,也是Assignable的子概念翎承。
術(shù)語:模型
模型(model):符合一個概念的數(shù)據(jù)類型稱為該概念的模型,例如:
int型是Comparable概念的模型符匾。
靜態(tài)數(shù)組類型不是Assignable概念的模型(無法用“=”給整個靜態(tài)數(shù)組賦值)STL:由迭代器叨咖,函數(shù)對象,容器啊胶,算法四部分組成
迭代器:從功能上可理解為一個泛型指針甸各,
//求平方的函數(shù)
double square(double x) {
return x * x;
}
int main() {
//從標準輸入讀入若干個實數(shù),分別將它們的平方輸出
transform(istream_iterator<double>(cin), istream_iterator<double>(),
ostream_iterator<double>(cout, "\t"), square);
cout << endl;
return 0;
}
//程序涉及到輸入迭代器创淡、輸出迭代器痴晦、隨機訪問迭代器這三個迭代器概念,并且以前兩個概念為基礎(chǔ)編寫了一個通用算法琳彩。
#include <algorithm>
#include <iterator>
#include <vector>
#include <iostream>
using namespace std;
//將來自輸入迭代器的n個T類型的數(shù)值排序誊酌,將結(jié)果通過輸出迭代器result輸出
template <class T, class InputIterator, class OutputIterator>
void mySort(InputIterator first, InputIterator last, OutputIterator result) {
//通過輸入迭代器將輸入數(shù)據(jù)存入向量容器s中
vector<T> s;
for (;first != last; ++first)
s.push_back(*first);
//對s進行排序,sort函數(shù)的參數(shù)必須是隨機訪問迭代器
sort(s.begin(), s.end());
copy(s.begin(), s.end(), result); //將s序列通過輸出迭代器輸出
}
int main() {
//將s數(shù)組的內(nèi)容排序后輸出
double a[5] = { 1.2, 2.4, 0.8, 3.3, 3.2 };
mySort<double>(a, a + 5, ostream_iterator<double>(cout, " "));
cout << endl;
//從標準輸入讀入若干個整數(shù)露乏,將排序后的結(jié)果輸出
mySort<int>(istream_iterator<int>(cin), istream_iterator<int>(), ostream_iterator<int>(cout, " "));
cout << endl;
return 0;
}
/*
-
容器:
容器的通用功能
用默認構(gòu)造函數(shù)構(gòu)造空容器
支持關(guān)系運算符:==碧浊、!=、<瘟仿、<=箱锐、>、>=
begin()劳较、end():獲得容器首驹止、尾迭代器
clear():將容器清空
empty():判斷容器是否為空
size():得到容器元素個數(shù)
s1.swap(s2):將s1和s2兩容器內(nèi)容交換
相關(guān)數(shù)據(jù)類型(S表示容器類型)
S::iterator:指向容器元素的迭代器類型
S::const_iterator:常迭代器類型
可逆容器
STL為每個可逆容器都提供了逆向迭代器,逆向迭代器可以通過下面的成員函數(shù)得到:
rbegin() :指向容器尾的逆向迭代器
rend():指向容器首的逆向迭代器
逆向迭代器的類型名的表示方式如下:
S::reverse_iterator:逆向迭代器類型
S::constreverseiterator:逆向常迭代器類
隨機訪問容器
隨機訪問容器支持對容器的元素進行隨機訪問
s[n]:獲得容器s的第n個元素 順序容器的基本操作
#include <iostream>
#include <list>
#include <deque>
//輸出指定的順序容器的元素
template <class T>
void printContainer(const char* msg, const T& s) {
cout << msg << ": ";
copy(s.begin(), s.end(), ostream_iterator<int>(cout, " "));
cout << endl;
}
int main() {
//從標準輸入讀入10個整數(shù)观蜗,將它們分別從s的頭部加入
deque<int> s;
for (int i = 0; i < 10; i++) {
int x;
cin >> x;
s.push_front(x);
}
printContainer("deque at first", s);
//用s容器的內(nèi)容的逆序構(gòu)造列表容器l
list<int> l(s.rbegin(), s.rend());
printContainer("list at first", l);
//將列表容器l的每相鄰兩個元素順序顛倒
list<int>::iterator iter = l.begin();
while (iter != l.end()) {
int v = *iter;
iter = l.erase(iter);
l.insert(++iter, v);
}
printContainer("list at last", l);
//用列表容器l的內(nèi)容給s賦值臊恋,將s輸出
s.assign(l.begin(), l.end());
printContainer("deque at last", s);
return 0;
}
int main() {
istream_iterator<int> i1(cin), i2; //建立一對輸入流迭代器
vector<int> s1(i1, i2); //通過輸入流迭代器從標準輸入流中輸入數(shù)據(jù)
sort(s1.begin(), s1.end()); //將輸入的整數(shù)排序
deque<int> s2;
//以下循環(huán)遍歷s1
for (vector<int>::iterator iter = s1.begin(); iter != s1.end(); ++iter)
{
if (*iter % 2 == 0) //偶數(shù)放到s2尾部
s2.push_back(*iter);
else //奇數(shù)放到s2首部
s2.push_front(*iter);
}
//將s2的結(jié)果輸出
copy(s2.begin(), s2.end(), ostream_iterator<int>(cout, " "));
cout << endl;
return 0;
}
STL所提供的順序容器各有所長也各有所短,我們在編寫程序時應當根據(jù)我們對容器所需要執(zhí)行的操作來決定選擇哪一種容器墓捻。
如果需要執(zhí)行大量的隨機訪問操作抖仅,而且當擴展容器時只需要向容器尾部加入新的元素,就應當選擇向量容器vector砖第;
如果需要少量的隨機訪問操作撤卢,需要在容器兩端插入或刪除元素,則應當選擇雙端隊列容器deque梧兼;
如果不需要對容器進行隨機訪問放吩,但是需要在中間位置插入或者刪除元素,就應當選擇列表容器list或forward_list羽杰;
如果需要數(shù)組屎慢,array相對于內(nèi)置數(shù)組類型而言瞭稼,是一種更安全、更容易使用的數(shù)組類型腻惠。
順序容器的插入迭代器
用于向容器頭部环肘、尾部或中間指定位置插入元素的迭代器
包括前插迭代器(frontinserter)、后插迭代器(backinsrter)和任意位置插入迭代器(inserter).
- 集合
輸入一串實數(shù)集灌,將重復的去掉悔雹,取最大和最小者的中值,分別輸出小于等于此中值和大于等于此中值的實數(shù)
//10_9.cpp
#include <set>
#include <iterator>
#include <utility>
#include <iostream>
using namespace std;
int main() {
set<double> s;
while (true) {
double v;
cin >> v;
if (v == 0) break; //輸入0表示結(jié)束
//嘗試將v插入
pair<set<double>::iterator,bool> r=s.insert(v);
if (!r.second) //如果v已存在欣喧,輸出提示信息
cout << v << " is duplicated" << endl;
}
//得到第一個元素的迭代器
set<double>::iterator iter1=s.begin();
//得到末尾的迭代器
set<double>::iterator iter2=s.end();
//得到最小和最大元素的中值
double medium=(*iter1 + *(--iter2)) / 2;
//輸出小于或等于中值的元素
cout<< "<= medium: "
copy(s.begin(), s.upper_bound(medium), ostream_iterator<double>(cout, " "));
cout << endl;
//輸出大于或等于中值的元素
cout << ">= medium: ";
copy(s.lower_bound(medium), s.end(), ostream_iterator<double>(cout, " "));
cout << endl;
return 0;
}
- map
統(tǒng)計一句話中每個字母出現(xiàn)的次數(shù)
// 10_11.cpp
#include <iostream>
#include <map>
#include <cctype>
using namespace std;
int main() {
map<char, int> s; //用來存儲字母出現(xiàn)次數(shù)的映射
char c; //存儲輸入字符
do {
cin >> c; //輸入下一個字符
if (isalpha(c)){ //判斷是否是字母
c = tolower(c); //將字母轉(zhuǎn)換為小寫
s[c]++; //將該字母的出現(xiàn)頻率加1
}
} while (c != '.'); //碰到“.”則結(jié)束輸入
//輸出每個字母出現(xiàn)次數(shù)
for (map<char, int>::iterator iter = s.begin(); iter != s.end(); ++iter)
cout << iter->first << " " << iter->second << " ";
cout << endl;
return 0;
}
- multiset 和multimap
//10_12.cpp
#include <iostream>
#include <map>
#include <utility>
#include <string>
using namespace std;
int main() {
multimap<string, string> courses;
typedef multimap<string, string>::iterator CourseIter;
//將課程上課時間插入courses映射中
courses.insert(make_pair("C++", "2-6"));
courses.insert(make_pair("COMPILER", "3-1"));
courses.insert(make_pair("COMPILER", "5-2"));
courses.insert(make_pair("OS", "1-2"));
courses.insert(make_pair("OS", "4-1"));
courses.insert(make_pair("OS", "5-5"));
//輸入一個課程名腌零,直到找到該課程為止,記下每周上課次數(shù)
string name;
int count;
do {
cin >> name;
count = courses.count(name);
if (count == 0)
cout << "Cannot find this course!" << endl;
} while (count == 0);
//輸出每周上課次數(shù)和上課時間
cout << count << " lesson(s) per week: ";
pair<CourseIter, CourseIter> range = courses.equal_range(name);
for (CourseIter iter = range.first; iter != range.second; ++iter)
cout << iter->second << " ";
cout << endl;
return 0;
}
- 函數(shù)對象:
STL提供的函數(shù)對象
用于算術(shù)運算的函數(shù)對象:
一元函數(shù)對象(一個參數(shù)) :negate
二元函數(shù)對象(兩個參數(shù)) :plus唆阿、minus益涧、multiplies、divides驯鳖、modulus
用于關(guān)系運算闲询、邏輯運算的函數(shù)對象(要求返回值為bool)
一元謂詞(一個參數(shù)):logical_not
二元謂詞(兩個參數(shù)):equalto、notequalto浅辙、greater扭弧、less、greaterequal记舆、lessequal鸽捻、logicaland、logical_or
#include <funtional>
sort(a.begin(), a.end(), greater<int>());
cout << accumulate(a, a + N, 1, multiplies<int>());
cout << accumulate(a, a + N, 1, mult)
- 函數(shù)適配器(感覺很難懂泽腮,不知怎么用御蒲,把代碼全都搞上來了)
- 綁定適配器:bind1st、bind2nd
將n元函數(shù)對象的指定參數(shù)綁定為一個常數(shù)诊赊,得到n-1元函數(shù)對象 - 組合適配器:not1厚满、not2
將指定謂詞的結(jié)果取反 - 函數(shù)指針適配器:ptr_fun
將一般函數(shù)指針轉(zhuǎn)換為函數(shù)對象,使之能夠作為其它函數(shù)適配器的輸入豪筝。
在進行參數(shù)綁定或其他轉(zhuǎn)換的時候,通常需要函數(shù)對象的類型信息摘能,例如bind1st和bind2nd要求函數(shù)對象必須繼承于binary_function類型续崖。但如果傳入的是函數(shù)指針形式的函數(shù)對象,則無法獲得函數(shù)對象的類型信息团搞。 - 成員函數(shù)適配器:ptrfun严望、ptrfun_ref
對成員函數(shù)指針使用,把n元成員函數(shù)適配為n + 1元函數(shù)對象逻恐,該函數(shù)對象的第一個參數(shù)為調(diào)用該成員函數(shù)時的目的對象
也就是需要將“object->method()”轉(zhuǎn)為“method(object)”形式像吻。將“object->method(arg1)”轉(zhuǎn)為二元函數(shù)“method(object, arg1)”峻黍。
- 綁定適配器:bind1st、bind2nd
//數(shù)適配器實例——找到數(shù)組中第一個大于40的元素
int main() {
int intArr[] = { 30, 90, 10, 40, 70, 50, 20, 80 };
const int N = sizeof(intArr) / sizeof(int);
vector<int> a(intArr, intArr + N);
vector<int>::iterator p = find_if(a.begin(), a.end(), bind2nd(greater<int>(), 40));
if (p == a.end())
cout << "no element greater than 40" << endl;
else
cout << "first element greater than 40 is: " << *p << endl;
return 0;
}
注:
find_if算法在STL中的原型聲明為:
template<class InputIterator, class UnaryPredicate>
InputIterator find_if(InputIterator first, InputIterator last, UnaryPredicate pred);
它的功能是查找數(shù)組[first, last)區(qū)間中第一個pred(x)為真的元素。
//ptr_fun拨匆、not1和not2產(chǎn)生函數(shù)適配器實例
bool g(int x, int y) {
return x > y;
}
int main() {
int intArr[] = { 30, 90, 10, 40, 70, 50, 20, 80 };
const int N = sizeof(intArr) / sizeof(int);
vector<int> a(intArr, intArr + N);
vector<int>::iterator p;
p = find_if(a.begin(), a.end(), bind2nd(ptr_fun(g), 40));
if (p == a.end())
cout << "no element greater than 40" << endl;
else
cout << "first element greater than 40 is: " << *p << endl;
p = find_if(a.begin(), a.end(), not1(bind2nd(greater<int>(), 15)));
if (p == a.end())
cout << "no element is not greater than 15" << endl;
else
cout << "first element that is not greater than 15 is: " << *p << endl;
p = find_if(a.begin(), a.end(), bind2nd(not2(greater<int>()), 15));
if (p == a.end())
cout << "no element is not greater than 15" << endl;
else
cout << "first element that is not greater than 15 is: " << *p << endl;
return 0;
}
// 成員函數(shù)適配器實例
struct Car {
int id;
Car(int id) { this->id = id; }
void display() const { cout << "car " << id << endl; }
};
int main() {
vector<Car *> pcars;
vector<Car> cars;
for (int i = 0; i < 5; i++)
pcars.push_back(new Car(i));
for (int i = 5; i < 10; i++)
cars.push_back(Car(i));
cout << "elements in pcars: " << endl;
for_each(pcars.begin(), pcars.end(), std::mem_fun(&Car::display));
cout << endl;
cout << "elements in cars: " << endl;
for_each(cars.begin(), cars.end(), std::mem_fun_ref(&Car::display));
cout << endl;
for (size_t i = 0; i < pcars.size(); ++i)
delete pcars[i];
return 0;
}
- STL算法(有待于從C++primer上做理解性的補充)
STL算法分類
不可變序列算法
可變序列算法
排序和搜索算法
數(shù)值算法
<utility>頭文件姆涩,將>=, <=, >都轉(zhuǎn)化為對 < 的調(diào)用,惭每!=則轉(zhuǎn)換為==的調(diào)用骨饿,因此我們在操作符重載時可以只重載 < 和=兩個操作符。同時要打開
using namespace std::rel_ops
刪除器代碼片段:可以結(jié)合for_each刪除區(qū)間內(nèi)的所有指針
struct deleter{
template<class T>
void operator()(T* p){ delete p;}
};
for_each(container.begin(), container.end(), deleter());
- 唯一化數(shù)組元素并排序輸出的代碼段
sort(nums.begin(), nums.end());
auto end_unique = unique(nums.begin(), nums.end());
nums.erase(end_unique, nums.end());
copy(nums.begin(),nums.end(), ostream_iterator<int>(cout, "\n"));
substr(index, num)
表示從index開始截取num長的子串并返回count函數(shù)和count_if函數(shù):功能類似于find台腥。這個函數(shù)使用一對迭代器和一個值做參數(shù)宏赘,返回這個值出現(xiàn)次數(shù)的統(tǒng)計結(jié)果。
count(ivec.begin() , ivec.end() , searchValue)
bool greater10(int value)
{
return value >10;
}
result1 = count_if(v1.begin(), v1.end(), greater10);
- 輸出流
cout輸出的格式控制 :使用操縱符黎侈,除了一次性的外察署,多數(shù)操縱符作用時間是直到下一次狀態(tài)改變
#include <iomanip>
cout<<setw(10)<<nums[i]<<endl;//指定寬度,一次性的
cout << setiosflags(ios_base::left)<<nums[i]//左對齊
cout <<resetiosflags(ios_base::left)//取消左對齊的方式峻汉,恢復到默認右對齊贴汪,
cout<< setprecision(1) << values[i] << endl;//設置有效數(shù)字位數(shù)
cout<<setiosflags(ios_base::fixd)<< setprecision(1) << values[i] << endl;//設置有效數(shù)字
將流寫入二進制文件中:當待存文件無需供人閱讀時可以選用二進制的這種方式,讀入讀出效率都非常高
#include <fstream>
using namespace std;
struct Date {
int mon, day, year;
};
int main() {
Date dt = { 6, 10, 92 };
ofstream file("date.dat", ios_base::binary);
file.write(reinterpret_cast<char *>(&dt),sizeof(dt));
file.close();
return 0;
}
字符串輸出流:典型應用是將數(shù)值轉(zhuǎn)換為字符串俱济,對于自定義類型嘶是,則必須重載相應的操作符如<<來使用
//11_6.cpp
#include <iostream>
#include <sstream>
#include <string>
using namespace std;
//函數(shù)模板toString可以將各種支持“<<“插入符的類型的對象轉(zhuǎn)換為字符串。
template <class T>
inline string toString(const T &v) {
ostringstream os; //創(chuàng)建字符串輸出流
os << v; //將變量v的值寫入字符串流
return os.str(); //返回輸出流生成的字符串
}
int main() {
string str1 = toString(5);
cout << str1 << endl;
string str2 = toString(1.2);
cout << str2 << endl;
return 0;
}
- 輸入流: get 讀可以帶空字符蛛碌,cin讀不出空字符聂喇,getline則可以讀出帶空格的字符串。
例11-7 get函數(shù)應用舉例
//11_7.cpp
#include <iostream>
using namespace std;
int main() {
char ch;
while ((ch = cin.get()) != EOF)//注意這里的EOF在unix下應該是crtl+Z那種東西
cout.put(ch);
return 0;
}
例11-8為輸入流指定一個終止字符:
//11_8.cpp
#include <iostream>
#include <string>
using namespace std;
int main() {
string line;
cout << "Type a line terminated by 't' " << endl;
getline(cin, line, 't');
cout << line << endl;
return 0;
}
例11-9 從文件讀一個二進制記錄到一個結(jié)構(gòu)中
//11_9.cpp
#include <iostream>
#include <fstream>
#include <cstring>
using namespace std;
struct SalaryInfo {
unsigned id;
double salary;
};
int main() {
SalaryInfo employee1 = { 600001, 8000 };
ofstream os("payroll", ios_base::out | ios_base::binary);
os.write(reinterpret_cast<char *>(&employee1), sizeof(employee1));
os.close();
ifstream is("payroll", ios_base::in | ios_base::binary);
if (is) {
SalaryInfo employee2;
is.read(reinterpret_cast<char *>(&employee2), sizeof(employee2));
cout << employee2.id << " " << employee2.salary << endl;
} else {
cout << "ERROR: Cannot open file 'payroll'." << endl;
}
is.close();
return 0;
}
例11-10用seekg函數(shù)設置位置指針
//11_10.cpp, 頭部分省略
int main() {
int values[] = { 3, 7, 0, 5, 4 };
ofstream os("integers", ios_base::out | ios_base::binary);
os.write(reinterpret_cast<char *>(values), sizeof(values));
os.close();
ifstream is("integers", ios_base::in | ios_base::binary);
if (is) {
is.seekg(3 * sizeof(int));
int v;
is.read(reinterpret_cast<char *>(&v), sizeof(int));
cout << "The 4th integer in the file 'integers' is " << v << endl;
} else {
cout << "ERROR: Cannot open file 'integers'." << endl;
}
return 0;
}
例11-11 讀一個文件并顯示出其中0元素的位置
//11_11.cpp, 頭部分省略
int main() {
ifstream file("integers", ios_base::in | ios_base::binary);
if (file) {
while (file) {//讀到文件尾file為0
streampos here = file.tellg();
int v;
file.read(reinterpret_cast<char *>(&v), sizeof(int));
if (file && v == 0)
cout << "Position " << here << " is 0" << endl;
}
} else {
cout << "ERROR: Cannot open file 'integers'." << endl;
}
file.close();
return 0;
}
istringstream的使用
template <class T>
inline T fromString(const string &str) {
istringstream is(str); //創(chuàng)建字符串輸入流
T v;
is >> v; //從字符串輸入流中讀取變量v
return v; //返回變量v
}
int main() {
int v1 = fromString<int>("5");
cout << v1 << endl;
double v2 = fromString<double>("1.2");
cout << v2 << endl;
return 0;
}
輸出結(jié)果:
5
1.2
- setprecision(2)處理浮點數(shù)時會自動的進行四舍五入蔚携,如12.3456在setprecision(2)后是12.35希太,想要得到12.34怎么辦?用floor函數(shù)。
d=floor(d*100)/100;//處理后d=12.34
- 異常處理:首先要搞清楚為什么要引入異常處理酝蜒,首先是為了讓錯誤處理更加靈活誊辉,你這個模塊沒有資格處理錯誤時怎么辦。其次是為了模塊化的設計亡脑,集中處理異常堕澄,不打擾程序邏輯。在大型應用程序中異常處理機制更能凸顯優(yōu)勢霉咨,需要不斷應用學習蛙紫。
//12_3.cpp
#include <iostream>
#include <cmath>
#include <stdexcept>
using namespace std;
//給出三角形三邊長,計算三角形面積
double area(double a, double b, double c) throw (invalid_argument)
{
//判斷三角形邊長是否為正
if (a <= 0 || b <= 0 || c <= 0)
throw invalid_argument("the side length should be positive");
//判斷三邊長是否滿足三角不等式
if (a + b <= c || b + c <= a || c + a <= b)
throw invalid_argument("the side length should fit the triangle inequation");
//由Heron公式計算三角形面積
double s = (a + b + c) / 2;
return sqrt(s * (s - a) * (s - b) * (s - c));
}
int main() {
double a, b, c; //三角形三邊長
cout << "Please input the side lengths of a triangle: ";
cin >> a >> b >> c;
try {
double s = area(a, b, c); //嘗試計算三角形面積
cout << "Area: " << s << endl;
} catch (exception &e) {
cout << "Error: " << e.what() << endl;
}
return 0;
}