學(xué)堂在線鄭莉《c++程序設(shè)計語言》(基礎(chǔ) & 進(jìn)階)課程筆記

筆記呢只簡要記錄了老師講課的關(guān)鍵知識點和結(jié)構(gòu),然后積累了一些有用的代碼小片段和解釋性很強的例子,之所以沒有分章節(jié)是為了使用瀏覽器頁內(nèi)查找(ctrl+f/ command+f)時方便。
課程的編程作業(yè)的代碼我也上傳了gayhub(在這里呀https://github.com/BMR731/XueTangCplusplus).
有錯誤的地方希望大家指出啦,有一起繼續(xù)學(xué)習(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++風(fēng)格的安全類型轉(zhuǎn)換,如 int j = static_cast<int>(i);
  • dynamic_cast的轉(zhuǎn)換,dynamic_cast主要用于類層次間的上行轉(zhuǎn)換和下行轉(zhuǎn)換,還可以用于類之間的交叉轉(zhuǎn)換。在類層次間進(jìn)行上行轉(zhuǎn)換時,dynamic_cast和[static_cast]的效果是一樣的;
    在進(jìn)行下行轉(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ù)體進(jìn)行替換,節(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ù)不能進(jìn)行異常接口聲明。

inline double calArea(int r){//計算圓面積
    return PI*r*r;
}
  • 傳遞引用有兩個優(yōu)點:
    • 為了實現(xiàn)雙向修改
    • 為了節(jié)省傳遞的開銷,但又不允許修改源數(shù)據(jù),如復(fù)制構(gòu)造函數(shù)foo(const class &A){...},添加const關(guān)鍵詞即可
  • constexpr函數(shù):constexpr修飾的函數(shù)在其所有參數(shù)都是constexpr時,一定返回constexpr;并且只有一條return語句;好處是表達(dá)式的值可以在編譯時進(jìn)行確定。
constexpr int get_size(){ return 20;}
constexpr int foo = get_size();//此時可以確保foo是一個常量表達(dá)式
  • 默認(rèn)參數(shù)值:
int add(int x, int y=2, int z=3);//對的,默認(rèn)參數(shù)給的順序必須從右到左
int add(int x =1, int y, int z=3);//錯
  • 函數(shù)重載:注意一下,返回值和形參類型不能區(qū)分重載函數(shù)即可
  • 在自定義構(gòu)造函數(shù)后仍希望編譯器給出默認(rèn)參數(shù)的話,可這樣寫
    clock() = default;
  • 用初始化列表的方式賦值更快
Clock::Clock(int newH,int newM,int newS): hour(newH),minute(newM),  second(newS) {
  }
  • 設(shè)計類時,寫一個的默認(rèn)構(gòu)造函數(shù)是良好的設(shè)計規(guī)范。
  • 委托構(gòu)造函數(shù)就是可以在構(gòu)造函數(shù)中調(diào)用其他構(gòu)造函數(shù)的機制
  • 復(fù)制構(gòu)造函數(shù)
class 類名 {
public :
    類名(形參);//構(gòu)造函數(shù)
    類名(const  類名 &對象名);//復(fù)制構(gòu)造函數(shù)
    //       ...
};

類名::類( const  類名 &對象名)//復(fù)制構(gòu)造函數(shù)的實現(xiàn)
{    函數(shù)體    }

//如果不希望被復(fù)制
//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;  //指示編譯器不生成默認(rèn)復(fù)制構(gòu)造函數(shù)
private:
    int x, y; //私有數(shù)據(jù)
};

//復(fù)制構(gòu)造函數(shù)被調(diào)用的三種時機
//1  用一個對象初始化對象時
//2  形參和實參結(jié)合時
//3  return 語句返回一個無名對象時
  • 析構(gòu)函數(shù)
    完成對象被刪除前的一些清理工作。
    在對象的生存期結(jié)束的時刻系統(tǒng)自動調(diào)用它,然后再釋放此對象所屬的空間。
    如果程序中未聲明析構(gòu)函數(shù),編譯器將自動產(chǎn)生一個默認(rèn)的析構(gòu)函數(shù),其函數(shù)體為空。
class Point {     
public:
  Point(int xx,int yy);
  ~Point();
  //...其他函數(shù)原型
private:
  int x, y;
};
  • 前向引用申明:為了解決兩個類在定義時相互引用的情況,但又不能完美解決所有情況,如它可以解決充當(dāng)形參的情況,但不能解決充當(dāng)成員變量的情況,因為涉及到具體的字節(jié)數(shù)等細(xì)節(jié)問題。
class B;  //前向引用聲明
class A {
public:
  void f(B b);
};

class B {
public:
  void g(A a);
};
  • 類的靜態(tài)成員別忘了在類外進(jìn)行定義和初始化
class foo{
  private:
    static int count;
}
int foo::count =0;//this line don't forget;在類外進(jìn)行定義和初始化!
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ù)、而沒有什么操作的類型;人們習(xí)慣將結(jié)構(gòu)體的數(shù)據(jù)成員設(shè)為公有,因此這時用結(jié)構(gòu)體更方便
  • 聯(lián)合體:目的是存儲空間的共用,減少冗余和錯誤。
  • 枚舉類:實質(zhì)上就是強類型的枚舉,與簡單枚舉相比,防止沖突;類型要求嚴(yán)格;更加多樣的基本類型,
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)
  • 多文件結(jié)構(gòu)
    • .h文件:類的聲明
    • .cpp文件: 類的實現(xiàn)
    • main()所在文件:類的使用文件
  • 預(yù)編譯指令:
#include
#define
#if....#endif 條件編譯
#if..#elif....#else...#endif
#ifdef.. #endif 如果標(biāo)記被定義過
#ifndef....#endif 如果標(biāo)記未被定義過

最常用的用法在類的聲明文件中,為了避免重復(fù)編譯,常這樣寫:
#ifndef CLIENT_H
#define CLIENT_H
...類的聲明
#endif
  • 指針相關(guān)
const int* p = &i; //表明p為只讀指針,但指針本身可以指向其他地方
int* const p = &i;//表明指針本身只能指向i的地址,但可對i進(jìn)行讀寫操作

//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ù)雙向傳遞時(引用也可以達(dá)到此效果)
    需要傳遞一組數(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 :不允許多個指針共享資源,可以用標(biāo)準(zhǔn)庫中的move函數(shù)轉(zhuǎn)移指針
    shared_ptr :多個指針共享資源
    weak_ptr :可復(fù)制shared_ptr,但其構(gòu)造或者釋放對資源不產(chǎn)生影響

  • 深層復(fù)制和淺層復(fù)制
    當(dāng)類成員為指針變量時,如一個數(shù)組,淺層復(fù)制只是復(fù)制了指針的值,而深層復(fù)制才可以復(fù)制指針?biāo)傅膬?nèi)容,此時多需要重寫復(fù)制拷貝函數(shù)

  • 移動構(gòu)造函數(shù):在一些情況下,不需要復(fù)制構(gòu)造時,而只需簡單移動,將控制權(quán)轉(zhuǎn)移給目標(biāo)對象時,可使用移動構(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成員;
    通過派生類的對象:不能直接訪問從基類繼承的任何成員。
    保護(hù)繼承(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)換
    公有派生類對象可以被當(dāng)作基類的對象使用,反之則不可。
    派生類的對象可以隱含轉(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;
}
  • 派生類的復(fù)制構(gòu)造函數(shù)
    一般都要為基類的復(fù)制構(gòu)造函數(shù)傳遞參數(shù)。
    復(fù)制構(gòu)造函數(shù)只能接受一個參數(shù),既用來初始化派生類定義的成員,也將被傳遞給基類的復(fù)制構(gòu)造函數(shù)。
    基類的復(fù)制構(gòu)造函數(shù)形參類型是基類對象的引用,實參可以是派生類對象的引用
    例如: C::C(const C &c1): B(c1) {…}

  • 派生類的析構(gòu)函數(shù):無需顯示調(diào)用,析構(gòu)次序與初始化次序相反。

  • 二義性:二義性可以發(fā)生在父子之間,父親之間,簡單的解決方案是使用類名加以限定即可,然而在多繼承下的二義性問題比較復(fù)雜,因此引入了虛基類,當(dāng)多個父親有一個共同的祖先時,祖先里的成員存在不一致性和冗余的風(fēng)險,因此通過虛繼承來保證祖先中的值只有一份,并且祖先的構(gòu)造函數(shù)需要在每一代的構(gòu)造函數(shù)中傳參,但實質(zhì)上執(zhí)行的只有最遠(yuǎn)派生類調(diào)用的構(gòu)造函數(shù)。

  • C++中class聲明的默認(rèn)權(quán)限是private,struct聲明的默認(rèn)權(quán)限是public

  • 重載運算符,重載函數(shù)可以重載為類內(nèi)成員函數(shù),或者類外函數(shù)。

重載為類內(nèi)成員函數(shù)的要求是,操作符的第一個參數(shù)必須是該類的類型。

//雙目運算符的重載
//例8-1復(fù)數(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;//返回值的本身引用,可以當(dāng)左值被修改
}
//重載后置++
Clock Clock::operator ++ (int) {
    //注意形參表中的整型參數(shù)
    Clock old = *this;
    ++(*this);  //調(diào)用前置“++”運算符
    return old;//返回值的一個副本,只能做右值,不能做觸及到本身的修改
}

重載為非成員函數(shù)的規(guī)則
函數(shù)的形參代表依自左至右次序排列的各操作數(shù)。
重載為非成員函數(shù)時,參數(shù)個數(shù)=原操作數(shù)個數(shù)(后置++、--除外)
至少應(yīng)該有一個自定義類型的參數(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;  //復(fù)數(shù)實部
        double imag;  //復(fù)數(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的使用可以使編譯器在編譯時進(jìn)行檢查,以免發(fā)生難以調(diào)試的運行時錯誤,要習(xí)慣使用。

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)重載了相應(yīng)的運算符。
//函數(shù)模板
template <typename T>
T add(T x, T y){
    return x+y;
}
//類模板
template <class T>
class Foo{
  T element;
   Foo();
}
Foo<T>::Foo(){}//注意此時在類外標(biāo)注類名時要把模板參數(shù)帶上,寫成Foo<T>::
  • 術(shù)語:概念
    用來界定具備一定功能的數(shù)據(jù)類型。例如:
    將“可以比大小的所有數(shù)據(jù)類型(有比較運算符)”這一概念記為Comparable
    將“具有公有的復(fù)制構(gòu)造函數(shù)并可以用‘=’賦值的數(shù)據(jù)類型”這一概念記為Assignable
    將“可以比大小、具有公有的復(fù)制構(gòu)造函數(shù)并可以用‘=’賦值的所有數(shù)據(jù)類型”這個概念記作Sortable
    對于兩個不同的概念A(yù)和B,如果概念A(yù)所需求的所有功能也是概念B所需求的功能,那么就說概念B是概念A(yù)的子概念。例如:
    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() {
    //從標(biāo)準(zhǔn)輸入讀入若干個實數(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進(jìn)行排序,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;
    //從標(biāo)準(zhǔn)輸入讀入若干個整數(shù),將排序后的結(jié)果輸出
    mySort<int>(istream_iterator<int>(cin), istream_iterator<int>(), ostream_iterator<int>(cout, " "));
    cout << endl;
    return 0;
}
/*
  • 容器:

    容器的分類.png

    容器的通用功能
    用默認(rèn)構(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:逆向常迭代器類
    隨機訪問容器
    隨機訪問容器支持對容器的元素進(jìn)行隨機訪問
    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() {
    //從標(biāo)準(zhǔn)輸入讀入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); //通過輸入流迭代器從標(biāo)準(zhǔn)輸入流中輸入數(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所提供的順序容器各有所長也各有所短,我們在編寫程序時應(yīng)當(dāng)根據(jù)我們對容器所需要執(zhí)行的操作來決定選擇哪一種容器。
如果需要執(zhí)行大量的隨機訪問操作,而且當(dāng)擴展容器時只需要向容器尾部加入新的元素,就應(yīng)當(dāng)選擇向量容器vector;
如果需要少量的隨機訪問操作,需要在容器兩端插入或刪除元素,則應(yīng)當(dāng)選擇雙端隊列容器deque;
如果不需要對容器進(jìn)行隨機訪問,但是需要在中間位置插入或者刪除元素,就應(yīng)當(dāng)選擇列表容器list或forward_list;
如果需要數(shù)組,array相對于內(nèi)置數(shù)組類型而言,是一種更安全、更容易使用的數(shù)組類型。
順序容器的插入迭代器
用于向容器頭部、尾部或中間指定位置插入元素的迭代器
包括前插迭代器(frontinserter)、后插迭代器(backinsrter)和任意位置插入迭代器(inserter).

  • 集合
輸入一串實數(shù),將重復(fù)的去掉,取最大和最小者的中值,分別輸出小于等于此中值和大于等于此中值的實數(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ù)適配器的輸入。
      在進(jìn)行參數(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)”。
//數(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)//取消左對齊的方式,恢復(fù)到默認(rèn)右對齊,
cout<< setprecision(1) << values[i] << endl;//設(shè)置有效數(shù)字位數(shù)

cout<<setiosflags(ios_base::fixd)<< setprecision(1) << values[i] << endl;//設(shè)置有效數(shù)字

將流寫入二進(jìn)制文件中:當(dāng)待存文件無需供人閱讀時可以選用二進(jìn)制的這種方式,讀入讀出效率都非常高

#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;
}

字符串輸出流:典型應(yīng)用是將數(shù)值轉(zhuǎn)換為字符串,對于自定義類型,則必須重載相應(yīng)的操作符如<<來使用

//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ù)應(yīng)用舉例
//11_7.cpp
#include <iostream>
using namespace std;
int main() {
    char ch;
    while ((ch = cin.get()) != EOF)//注意這里的EOF在unix下應(yīng)該是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 從文件讀一個二進(jìn)制記錄到一個結(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ù)設(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ù)時會自動的進(jìn)行四舍五入,如12.3456在setprecision(2)后是12.35,想要得到12.34怎么辦?用floor函數(shù)。
d=floor(d*100)/100;//處理后d=12.34
  • 異常處理:首先要搞清楚為什么要引入異常處理,首先是為了讓錯誤處理更加靈活,你這個模塊沒有資格處理錯誤時怎么辦。其次是為了模塊化的設(shè)計,集中處理異常,不打擾程序邏輯。在大型應(yīng)用程序中異常處理機制更能凸顯優(yōu)勢,需要不斷應(yīng)用學(xué)習(xí)。
//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;
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 前言 把《C++ Primer》[https://book.douban.com/subject/25708312...
    尤汐Yogy閱讀 9,667評論 1 51
  • 3. 類設(shè)計者工具 3.1 拷貝控制 五種函數(shù)拷貝構(gòu)造函數(shù)拷貝賦值運算符移動構(gòu)造函數(shù)移動賦值運算符析構(gòu)函數(shù)拷貝和移...
    王偵閱讀 2,071評論 0 1
  • 1.C和C++的區(qū)別?C++的特性?面向?qū)ο缶幊痰暮锰帲?答:c++在c的基礎(chǔ)上增添類,C是一個結(jié)構(gòu)化語言,它的重...
    杰倫哎呦哎呦閱讀 9,995評論 0 45
  • C++文件 例:從文件income. in中讀入收入直到文件結(jié)束,并將收入和稅金輸出到文件tax. out。 檢查...
    SeanC52111閱讀 3,093評論 0 3
  • 圓月殘月盡是月, 肚困意困思家困。 臥躺北嶺望惠來, 誰知已是半夜時。
    LoveButterfly閱讀 228評論 4 1

友情鏈接更多精彩內(nèi)容