1. 程式人生 > >C++入門學習例項

C++入門學習例項

C++面向物件:

  類,是建立物件的模板,一個類可以建立多個相同的物件;物件,是類的例項,是按照類的規則建立的。
  類是抽象的,不佔用記憶體,而物件是具體的,佔用儲存空間。
  用struct宣告的結構體型別實際上也就是類。用struct宣告的類,如果對其成員不作private或public的宣告,系統將其預設為public。而用class定義的類,如果不作private或public宣告,系統將其成員預設為private,在需要時也可以自己用顯式宣告改變。如果希望成員是公用的,使用struct比較方便,如果希望部分成員是私有的,宜用class。

#include "stdafx.h"
#include <iostream> using namespace std; // 使用名稱空間 class People{ public: void say(){ cout<<"Hello"<<endl; } }; //類的宣告必須以分號結束! int main() { People *p=new People();//指標物件 p->say(); delete p; People pel; //值物件 pel.say(); People *q=&pel; //指向物件的指標
q->say(); return 0; }

C++建構函式和解構函式

建立一個物件時,常常需要作某些初始化的工作,例如對資料成員賦初值。
注意,類的資料成員是不能在宣告類時初始化的。如果一個類中所有的成員都是公用的,則可以在定義物件時對資料成員進行初始化。如:

class Time
{
   public : //宣告為公用成員
   hour;
   minute;
   sec;
};
Time t1={14,56,30};  //將t1初始化為14:56:30

  這種情況和結構體變數的初始化是差不多的,在一個花括號內順序列出各公用資料成員的值,兩個值之間用逗號分隔。但是,如果資料成員是私有的,或者類中有private或protected的成員,就不能用這種方法初始化。
  
  為了解決這個問題,C++提供了建構函式(constructor)來處理物件的初始化。建構函式是一種特殊的成員函式,與其他成員函式不同,不需要使用者來呼叫它,而是在建立物件時自動執行。
  建構函式的名字必須與類名同名,而不能由使用者任意命名,以便編譯系統能識別它並把它作為建構函式處理。它不具有任何型別,不返回任何值。建構函式的功能是由使用者定義的,使用者根據初始化的要求設計函式體和函式引數。

  解構函式(destructor)也是一個特殊的成員函式,它的作用與建構函式相反,它的名字是類名的前面加一個“~”符號。當物件的生命期結束時,會自動執行解構函式。
  注意:解構函式不返回任何值,沒有函式型別,也沒有函式引數。因此它不能被過載。一個類可以有多個建構函式,但只能有一個解構函式。

#include "stdafx.h"
#include <iostream>
#include <string>
using namespace std;

class People{
private:
    int i;
public:
    People(int i){
        this->i=i;
    cout<<"建構函式被執行"<<i<<endl;
    }
    ~People(){
    cout<<"解構函式被執行"<<i<<endl;
    }
    void say(){
    cout<<"Hello,I am student"<<i<<endl;
    }
};

int main(){

    //People *p = new  People();          //建立指標物件時建構函式被執行
    //delete p;                            //刪除物件時解構函式被執行
    People p1(1);                          //建立值物件,自動呼叫建構函式和解構函式
    p1.say();
    People p2(2); 
    p2.say();
    return 0;
};

拷貝建構函式

  拷貝建構函式,又稱複製建構函式,是一種特殊的建構函式,它由編譯器呼叫來完成一些基於同一類的其他物件的構建及初始化。其唯一的形參必須是引用,但並不限制為const,一般普遍的會加上const限制。此函式經常用在函式呼叫時使用者定義型別的值傳遞及返回。拷貝建構函式要呼叫基類的拷貝建構函式和成員函式。如果可以的話,它將用常量方式呼叫,另外,也可以用非常量方式呼叫。

拷貝建構函式的形式:
Class X
{
public:
  X();
  X(const X &a);//拷貝建構函式
}

拷貝建構函式呼叫的三種形式:
1.一個物件作為函式引數,以值傳遞的方式傳入函式體;
2.一個物件作為函式返回值,以值傳遞的方式從函式返回;
3.一個物件用於給另外一個物件進行初始化(常稱為複製初始化)。

#include "StdAfx.h"
#include <iostream>
using namespace std;

class B{
private:
  int j;
public:
    B() {setI(1);}
    B(int k) {setI(k);}  //函式過載
    ~B(){}
    B(B &a)   {
        j=a.j;
        cout<<"拷貝構造被呼叫"<<endl;
    }

    int setI(int j){return this->j=j;}
    int getI(){return j;}

};
void fun1(B b){
    cout<<"fun1輸出:"<<b.getI()<<endl;
}
B fun2(){
    B b(3);
    return b;
}
int main( ){

B  b1(3);   //建立第一個物件b1
B  b2(b1);   //b1初始化b2,第一次呼叫拷貝建構函式
fun1(b2);   //物件b2作為fun1實參,第二次呼叫拷貝建構函式
fun2();    //函式返回值是類物件,第三次呼叫拷貝建構函式
return 0; 
}

C++類的繼承與派生

  在C++中,所謂“繼承”就是在一個已存在的類的基礎上建立一個新的類。已存在的類稱為“基類(base class)”或“父類(father class)”,新建的類稱為“派生類(derived class)”或“子類(son class )”。
  一個新類從已有的類那裡獲得其已有特性,這種現象稱為類的繼承。通過繼承,一個新建子類從已有的父類那裡獲得父類的特性。從另一角度說,從已有的類(父類)產生一個新的子類,稱為類的派生。
  一個派生類有兩個或多個基類的稱為多重繼承(multiple inheritance)。

  宣告派生類的一般形式為:
class 派生類名:[繼承方式] 基類名
  {
  派生類新增加的成員
   };

  
  繼承方式包括public(公用的)、private(私有的)和protected(受保護的),如果不寫此項,則預設為private(私有的)。
  1) 公用繼承(public inheritance):
  基類的公用成員和保護成員在派生類中保持原有訪問屬性,其私有成員仍為基類私有。
  2) 私有繼承(private inheritance):
  基類的公用成員和保護成員在派生類中成了私有成員,其私有成員仍為基類私有。
  3) 受保護的繼承(protected inheritance):
  基類的公用成員和保護成員在派生類中成了保護成員,其私有成員仍為基類私有。保護成員的意思是,不能被外界引用,但可以被派生類的成員引用。

#include "stdafx.h"
#include <iostream>
#include <string>
using namespace std;

class People{
    private:   
        string name;
    public: 
        void getName(string name){
            this->name=name;
            cout<<"Name:"<<name<<endl;
        }
};
class Student:public People{ //公有繼承
    private:
        int age;
    public: 
        void getAge(int age){
            this->age=age;
            cout<<"Age:"<<age<<endl;
        }
};

int main(){
    Student *p=new Student();
    p->getName("Zhang");//只能通過基類的公用成員函式來引用基類的私有資料成員
    p->getAge(12);
    delete p;
    return 0;
}

  此外,在宣告派生類時,一般還應當自己定義派生類的建構函式和解構函式,因為建構函式和解構函式是不能從基類繼承的。

C++多型性與虛擬函式、純虛擬函式、函式重寫、抽象類

  虛擬函式的作用是允許在派生類中重新定義與基類同名的函式,並且可以通過基類指標或引用來訪問基類和派生類中的同名函式。當把基類的某個成員函式宣告為虛擬函式後,允許在其派生類中對該函式重新定義,賦予它新的功能,並且可以通過指向基類的指標指向同一類族中不同類的物件,從而呼叫其中的同名函式。

虛擬函式的使用方法是:
1. 在基類用virtual宣告成員函式為虛擬函式。
2. 在派生類中重新定義此函式,要求函式名、函式型別、函式引數個數和型別全部與基類的虛擬函式相同,並根據派生類的需要重新定義函式體。
3. 定義一個指向基類物件的指標變數,並使它指向同一類族中需要呼叫該函式的物件。
4. 通過該指標變數呼叫此虛擬函式,此時呼叫的就是指標變數指向的物件的同名函式。
  需要說明;有時在基類中定義的非虛擬函式會在派生類中被重新定義,如果用基類指標呼叫該成員函式,則系統會呼叫物件中基類部分的成員函式;如果用派生類指標呼叫該成員函式,則系統會呼叫派生類物件中的成員函式,這並不是多型性行為(使用的是不同型別的指標),沒有用到虛擬函式的功能。

#include "stdafx.h"
#include <iostream>
#include <string>
using namespace std;

class People{

    public: 
        virtual void say(){     //定義為虛擬函式
        cout<<"People say."<<endl;
        }
};
class Student:public People{ 

    public:     
        void say(){            //函式重寫
        cout<<"Students say."<<endl;
        }
};

int main(){
    People p;
    Student s;
     People *pt=&p;//定義指向基類物件的指標變數pt;
     pt->say();
     pt=&s;        //可呼叫派生類中的同名函式;若之前無virtual關鍵字,執行基類方法
     pt->say();

    return 0;
}



  
  純虛擬函式是在宣告虛擬函式時被“初始化”為0的函式。宣告純虛擬函式的一般形式是
   
    virtual 函式型別 函式名 (引數表列) = 0;

關於純虛擬函式需要注意的幾點:
1. 純虛擬函式沒有函式體;
2. 最後面的“=0”並不表示函式返回值為0,它只起形式上的作用,告訴編譯系統“這是純虛擬函式”;
3. 這是一個宣告語句,最後應有分號。

  純虛擬函式的作用是在基類中為其派生類保留一個函式的名字,以便派生類根據需要對它進行定義。
  純虛擬函式只有函式的名字而不具備函式的功能,不能被呼叫。它只是通知編譯系統:“在這裡宣告一個虛擬函式,留待派生類中定義”。因為純虛擬函式是不能被呼叫的,包含純虛擬函式的類是無法建立物件的。在派生類中對此函式提供定義後,它才能具備函式的功能,可被呼叫。
  如果在基類中沒有保留函式名字,則無法實現多型性。如果在一個類中聲明瞭純虛擬函式,而在其派生類中沒有對該函式定義,則該虛擬函式在派生類中仍然為純虛擬函式

  凡是包含純虛擬函式的類都是抽象類這種不用來定義物件而只作為一種基本型別用作繼承的類,稱為抽象類(abstract class ),由於它常用作基類,通常稱為抽象基類(abstract base class )。
  抽象類的作用是作為一個類族的共同基類,或者說,為一個類族提供一個公共介面。一個類層次結構中當然也可不包含任何抽象類,每一層次的類都是實際可用的,可以用來建立物件的。

#include "stdafx.h"
#include <iostream>
#include <string>
using namespace std;

class People{   //抽象類
    public: 
        virtual void say()const=0;    //定義為純虛擬函式

};
class Student:public People{ 

    public:     
         void say()const{             //對純虛擬函式進行再定義
        cout<<"Students say."<<endl;
        }
};

int main(){

    Student s;
    s.say();   ////靜態關聯

    People *pt;  //定義基類指標
    pt=&s;   //指標指向Student類物件
    pt->say();  //動態關聯

    return 0;
}

C++運算子過載

C++中執行時的多型性主要是通過虛擬函式來實現的,而編譯時的多型性是由函式過載和運算子過載來實現的。
運算子過載規則如下:
1, C++中的運算子除了少數幾個之外,全部可以過載,而且只能過載C++中已有的運算子。
2, 過載之後運算子的優先順序和結合性都不會改變。
3, 運算子過載是針對新型別資料的實際需要,對原有運算子進行適當的改造。一般來說,過載的功能應當與原有功能相類似,不能改變原運算子的操作物件個數,同時至少要有一個操作物件是自定義型別。
4,不能過載的運算子只有五個,它們是:成員運算子“.”、指標運算子“*”、作用域運算子“::”、“sizeof”、條件運算子“?:”。
5,運算子過載形式有兩種,過載為類的成員函式過載為類的友元函式
6,運算子過載為類的成員函式的一般語法形式為:
  函式型別 operator 運算子(形參表)
  {
   函式體;
  }
運算子過載為類的友元函式的一般語法形式為:
  friend 函式型別 operator 運算子(形參表)
  {
   函式體;
  }
  其中,函式型別就是運算結果型別;operator是定義運算子過載函式的關鍵字;運算子是過載的運算子名稱。
  當運算子過載為類的成員函式時,函式的引數個數比原來的操作個數要少一個;當過載為類的友元函式時,引數個數與原運算元個數相同。原因是過載為類的成員函式時,如果某個物件使用過載了的成員函式,自身的資料可以直接訪問,就不需要再放在引數表中進行傳遞,少了的運算元就是該物件本身。而過載為友元函式時,友元函式對某個物件的資料進行操作,就必須通過該物件的名稱來進行,因此使用到的引數都要進行傳遞,運算元的個數就不會有變化。
  運算子過載的主要優點就是允許改變使用於系統內部的運算子的操作方式,以適應使用者自定義型別的類似運算。

過載為類的成員函式:

#include "StdAfx.h"
#include <iostream> 
using namespace std;

class complex
{
public:
complex() { real=imag=0; }       //建構函式初始化
complex(double r, double i){     //有參建構函式過載
real = r, imag = i;
}
complex operator +(const complex &c); //申明運算子過載
complex operator *(const complex &c);
friend void print(const complex &c);  //友元函式訪問私有變數

private:
double real, imag;
};

inline complex complex::operator +(const complex &c)  //行內函數提高函式的執行效率
{
return complex(real + c.real, imag + c.imag);
}

inline complex complex::operator *(const complex &c)
{
return complex(real * c.real - imag * c.imag, real * c.imag + imag * c.real);
}


void print(const complex &c)
{
if(c.imag<0)
cout<<c.real<<c.imag<<'i'<<endl;
else
cout<<c.real<<'+'<<c.imag<<'i'<<endl;
}

void main()
{
complex c1(2.0, 3.0), c2(4.0, -2.0), c3;
c3 = c1 + c2;
cout<<"c1+c2=";
print(c3);

c3 = c1 * c2;
cout<<"c1*c2=";
print(c3);

c3 = (c1+c2) * c1;
cout<<"(c1+c2)*c1=";
print(c3);
cout<<endl;
}

過載為類的友元函式:

#include "StdAfx.h"
#include <iostream> 
using namespace std;

class complex
{
public:
complex() { real=imag=0; }
complex(double r, double i)
{
real = r, imag = i;
}
friend complex operator +(const complex &c1, const complex &c2);
friend complex operator *(const complex &c1, const complex &c2);
friend void print(const complex &c);
private:
double real, imag;
};

complex operator +(const complex &c1, const complex &c2)
{
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, c1.real * c2.imag + c1.imag * c2.real);
}


void print(const complex &c)
{
if(c.imag<0)
cout<<c.real<<c.imag<<'i'<<endl;
else
cout<<c.real<<'+'<<c.imag<<'i'<<endl;
}

void main()
{
complex c1(2.0, 3.0), c2(4.0, -2.0), c3;
c3 = c1 + c2;
cout<<"c1+c2=";
print(c3);

c3 = c1 * c2;
cout<<"c1*c2=";
print(c3);

c3 = (c1+c2) * c1;
cout<<"(c1+c2)*c1=";
print(c3);

}

兩種過載形式的比較
  一般說來,單目運算子最好被過載為成員;對雙目運算子最好被過載為友元函式,雙目運算子過載為友元函式比過載為成員函式更方便,但是,有的雙目運算子還是過載為成員函式為好,例如,賦值運算子。因為,它如果被過載為友元函式,將會出現與賦值語義不一致的地方。

C++函式指標

  1. 定義
    每一個函式都佔用一段記憶體單元,它們有一個起始地址,指向函式入口地址的指標稱為函式指標。
  2. 語法
    指向函式的指標變數的一般定義形式為:
     資料型別 (*指標變數名)(引數表);
  3. 說明
    1) 函式指標的定義形式中的資料型別是指函式的返回值的型別。
    2) 區分下面兩個語句:
      int (*p)(int a, int b); //p是一個指向函式的指標變數,所指函式的返回值型別為整型
      int *p(int a, int b); //p是函式名,此函式的返回值型別為整型指標

    3) 指向函式的指標變數不是固定指向哪一個函式的,而只是表示定義了一個這樣型別的變數,它是專門用來存放函式的入口地址的;在程式中把哪一個函式的地址賦給它,它就指向哪一個函式。
    4) 在給函式指標變數賦值時,只需給出函式名,而不必給出引數。
    如函式max的原型為:int max(int x, int y); 指標p的定義為:int (*p)(int a, int b); 則p = max;的作用是將函式max的入口地址賦給指標變數p。這時,p就是指向函式max的指標變數,也就是p和max都指向函式的開頭。
    5) 在一個程式中,指標變數p可以先後指向不同的函式,但一個函式不能賦給一個不一致的函式指標(即不能讓一個函式指標指向與其型別不一致的函式)。
    如有如下的函式:int fn1(int x, int y); int fn2(int x);
    定義如下的函式指標:int (*p1)(int a, int b); int (*p2)(int a);

    p1 = fn1; //正確
    p2 = fn2; //正確
    p1 = fn2; //產生編譯錯誤
    6) 定義了一個函式指標並讓它指向了一個函式後,對函式的呼叫可以通過函式名呼叫,也可以通過函式指標呼叫(即用指向函式的指標變數呼叫)。如語句:
      c = (*p)(a, b); //表示呼叫由p指向的函式(max),實參為a,b,函式呼叫結束後得到的函式值賦給c
    7) 函式指標只能指向函式的入口處,而不可能指向函式中間的某一條指令。不能用*(p+1)來表示函式的下一條指令。
    8) 函式指標變數常用的用途之一是把指標作為引數傳遞到其他函式。
#include "StdAfx.h"
#include <iostream>   
#include <conio.h>  
using namespace std; 

int max(int x, int y); //求最大數  
int min(int x, int y); //求最小數  
int add(int x, int y); //求和  
void process(int i, int j, int (*p)(int a, int b)); //應用函式指標  
int max(int x, int y){  
    return x > y ? x : y;  
}    
int min(int x, int y){  
    return x > y ? y : x;  
}    
int add(int x, int y){  
    return x + y;  
}    
void process(int i, int j, int (*p)(int a, int b)){  
    cout<<p(i, j)<<endl;  
}  
int main()  
{  
    int x, y;  
    cin>>x>>y;  

    cout<<"Max is: ";  
    process(x, y, max);  

    cout<<"Min is: ";  
    process(x, y, min);  

    cout<<"Add is: ";  
    process(x, y, add);  

    return 0;  
}  

C++引用

  引用的兩個主要用途:作為函式引數以及從函式中返回左值
  引用引入了物件的一個同義詞。定義引用的表示方法與定義指標相似,只是用&代替了*。例如:
Point pt1(10,10);
Point &pt2=pt1;   //定義了pt2為pt1的引用。通過這樣的定義,pt1和pt2表示同一物件。
  需要特別強調的是引用並不產生物件的副本,僅僅是物件的同義詞。因此,當下面的語句執行後:
pt1.offset(2,2);  pt1和pt2都具有(12,12)的值。
  引用必須在定義時馬上被初始化,因為它必須是某個東西的同義詞。你不能先定義一個引用後才初始化它。例如下面語句是非法的:
  Point &pt3;
  pt3=pt1;

  引用和指標的比較:
(1)引用總是指向某個物件:定義引用時沒有初始化是錯誤的。而指標在定義時則可以不進行初始化;
(2)賦值行為的差異:給引用賦值修改的是該引用所關聯的物件的值,而並不是使引用與另一個物件關聯。而給指標賦值,則修改指標本身的值;
(3)引用一經初始化,就始終指向同一個特定物件,不能修改。而指標在定義後則可以修改;
(4)不存在指向空值的引用。而存在指向空值的指標。因此使用引用的程式碼效率比使用指標的要高,因為在使用引用之前不需要測試它的合法性。

  總體來說,在以下情況下你應該使用指標:
(1)考慮奧存在不指向任何物件的可能(在這種情況下,可以設定指標為空);
(2)需要在不同的時刻指向不同的物件(在這種情況下,可以改變指標的指向);

   總體來說,在以下情況你應該使用引用:
(1)如果總是指向一個物件並且一旦指向一個物件就不會再改變指向;
(2)當你過載某個操作符時,你應該使用引用,避免不必要的語義誤解;

#include "StdAfx.h"
#include <iostream> 
using namespace std;

int temp; //定義全域性變數temp

void swap(int &p1, int &p2){ //此處函式的形參p1, p2都是引用(引用作為引數)
int p;                         //它以返回值的方法返回函式值
p=p1;
p1=p2; 
p2=p; 
}

int &fn2(int r) //定義函式fn2,它以引用方式返回函式值
{
temp=(int)(r*r*3.14);
return temp;
}

int main( )
{
int a,b;
cin>>a>>b;   
swap(a,b);    //直接以變數a和b作為實參呼叫swap函式
cout<<a<<" "<< b <<endl; 

int c=fn2(a); //系統生成返回值的副本,可以從被調函式中返回一個全域性變數的引用
int &d=fn2(a); //系統不生成返回值的副本,可以從被調函式中返回一個全域性變數的引用
cout<<c<<endl;
cout<<d<<endl;
return 0;
}

引用作為返回值,必須遵守以下規則:

  (1)不能返回區域性變數的引用。這條可以參照Effective C++[1]的Item 31。主要原因是區域性變數會在函式返回後被銷燬,因此被返回的引用就成為了”無所指”的引用,程式會進入未知狀態。

  (2)不能返回函式內部new分配的記憶體的引用。這條可以參照Effective C++[1]的Item 31。雖然不存在區域性變數的被動銷燬問題,可對於這種情況(返回函式內部new分配記憶體的引用),又面臨其它尷尬局面。例如,被函式返回的引用只是作為一 個臨時變量出現,而沒有被賦予一個實際的變數,那麼這個引用所指向的空間(由new分配)就無法釋放,造成memory leak。

  (3)可以返回類成員的引用,但最好是const。這條原則可以參照Effective C++[1]的Item 30。主要原因是當物件的屬性是與某種業務規則(business rule)相關聯的時候,其賦值常常與某些其它屬性或者物件的狀態有關,因此有必要將賦值操作封裝在一個業務規則當中。如果其它物件可以獲得該屬性的非常 量引用(或指標),那麼對該屬性的單純賦值就會破壞業務規則的完整性。

  (4)引用與一些操作符的過載:
  流操作符<<和>>,這兩個操作符常常希望被連續使用,例如:cout << “hello” << endl; 因此這兩個操作符的返回值應該是一個仍然支援這兩個操作符的流引用。可選的其它方案包括:返回一個流物件和返回一個流物件指標。但是對於返回 一個流物件,程式必須重新(拷貝)構造一個新的流物件,也就是說,連續的兩個<<操作符實際上是針對不同物件的!這無法讓人接受。對於返回一 個流指標則不能連續使用<<操作符。因此,返回一個流物件引用是惟一選擇。這個唯一選擇很關鍵,它說明了引用的重要性以及無可替代性,也許這 就是C++語言中引入引用這個概念的原因吧。 賦值操作符=。這個操作符象流操作符一樣,是可以連續使用的,例如:x = j = 10;或者(x=10)=100;賦值操作符的返回值必須是一個左值,以便可以被繼續賦值。因此引用成了這個操作符的惟一返回值選擇。

C++友元函式和友元類

  如果在本類以外的其他地方定義了一個函式(這個函式可以是不屬於任何類的非成員函式,也可以是其他類的成員函式),在類體中用friend對其進行宣告,此函式就稱為本類的友元函式。友元函式可以訪問這個類中的私有成員:

#include "StdAfx.h"
#include <iostream>
using namespace std;
class Time
{
public:
  Time(int,int,int);
   friend void display(Time &);  //宣告display函式為Time類的友元函式
private:                          //以下資料是私有資料成員
   int hour;
   int minute;
   int sec;
};

Time::Time(int h,int m,int s)  //成員建構函式給hour,minute,sec賦初值
{
   hour=h;
   minute=m;
   sec=s;
}

void display(Time& t)  //這是友元函式,形參t是Time類物件的引用
{
   cout<<t.hour<<":"<<t.minute<<":"<<t.sec<<endl;
}

int main( )
{
   Time t1(10,13,56);
   display(t1);
   return 0;  //呼叫display函式,實參t1是Time類物件
}

  不僅可以將一個函式宣告為一個類的“朋友”,而且可以將一個類(例如B類)宣告為另一個類(例如A類)的“朋友”。這時B類就是A類的友元類。
使用友元類時注意:
(1)友元關係不能被繼承。
(2)友元關係是單向的,不具有交換性。若類B是類A的友元,類A不一定是類B的友元,要看在類中是否有相應的宣告。
(3)友元關係不具有傳遞性。若類B是類A的友元,類C是B的友元,類C不一定是類A的友元,同樣要看類中是否有相應的申明

C++標準庫容器的基本用法

  c++標準模版庫中,容器總共分為三類:順序容器、關聯容器、容器介面卡。
  
  1,標準庫的有序容器包括set、map、multiset、multimap四類,這類容器內部的元素始終是有序的,容器內部預設使用‘<’操作符完成元素大小的比較,使用者也可以提供自己的元素大小比較函式。這類容器增加元素只提供了insert操作,插入的元素由容器按其大小自動放到合適的位置。
  有序的容器的實現可以理解為是相同的,就是一棵平衡二叉樹。set和multiset中,樹節點包含的就是每個元素,並按元素的大小比較結果排序,set中不允許有相同的元素,而multiset則可以存在相同的元素。map和multimap中,每個樹節點就是map的每個元素的鍵和值,並按鍵的大小比較結果排序,map中不允許有相同鍵的元素,而multimap則可以存在相同鍵的元素。

  2,無序容器即內部元素是沒有排序的,但也不是亂序的,元素的順序為元素加入容器中的順序。這類容器包括vector、list、deque三類。這類容器提供了push_back和push_front操作,即在尾部或者是頭部插入元素,insert操作可以在容器指定的位置插入元素。

是否連續記憶體

支援頭部插入或刪除元素

支援中間插入或刪除元素

支援隨機訪問

vector

是(效率低)

list

是(效率很高)

deque

塊內連續

是(效率高)



 3,還有一種是容器介面卡。介面卡顧名思義,就是讓一個物件的行為符合另一個物件的行為的機制。容器介面卡,就是一個介面,它讓一種已存在的容器型別採用另一種不同的抽象型別的工作方式實現。c++ STL 中包含三種介面卡:棧stack 、佇列queue 和優先順序priority_queue。
  STL 中提供的三種介面卡可以由某一種順序容器去實現。預設下stack 和queue 基於deque 容器實現,priority_queue 則基於vector 容器實現。當然在建立一個介面卡時也可以指定具體的實現容器,建立介面卡時在第二個引數上指定具體的順序容器可以覆蓋介面卡的預設實現。
  棧stack 的特點是後進先出,所以它關聯的基本容器可以是任意一種順序容器,因為這些容器型別結構都可以提供棧的操作有求,它們都提供了push_back 、pop_back 和back 操作;
佇列queue 的特點是先進先出,介面卡要求其關聯的基礎容器必須提供pop_front 操作,因此其不能建立在vector 容器上;
  優先順序佇列priority_queue 介面卡要求提供隨機訪問功能,因此不能建立在list 容器上。

#include "StdAfx.h"
#include <iostream>
#include <string>
#include <list>
#include <map>
using namespace std;

    int main( ){
        list<string> l;               
        l.push_back("Hello");
        l.push_back("World");
        for(list<string>::iterator it=l.begin();it!=l.end();it++){
        cout<<*it<<endl;
        }

        map<string,string> m;
        m.insert(pair<string,string>("one","cat"));
        m.insert(pair<string,string>("two","horse"));
        cout<<m.at("one")<<endl;
        //使用[]過載運算子
        m["three"]="dog";
        cout<<m.at("three")<<endl;

       return 0; 
    }

C++字串常用操作

#include "StdAfx.h"
#include <iostream>
#include <string>
#include <sstream>
using namespace std;
int main( ){
string str;
str+="Hello";
str+="World";
cout<<str<<endl;

stringstream ss;
 ss<<200;
 ss<<" ";
 ss<<"Pigs";
 cout<<ss.str()<<endl;
 return 0; 
    }

C++檔案操作

#include "StdAfx.h"
#include <iostream>
#include <fstream>
#include <sstream>
using namespace std;

int main( ){
    ofstream of("a.txt");
    of<<"Hello World !";  //向檔案中寫入字串
    of.close();

    ifstream inf("a.txt");//讀取字串
    stringbuf sb;
    inf>>&sb;
    cout<<sb.str()<<endl;

    return 0; 
}

型別組合(結構,陣列和指標)

#include "stdafx.h"
#include <iostream>
using namespace std;

struct Year{                            //結構體
    int year;
    int month;
};

int main(){
    Year  y[3]={{1990,1},{1991,12}};//建立結構陣列,說明:y是一個數組,y[0]是一個結構,y[0].year是該結構的一個成員
    y[0].year=2000;              //使用成員運算子訪問其成員
    (y+1)->year=2001;           //由於陣列名是一個指標,等價於y[1].year
    cout<<(y+1)->year<<endl;
    cout<<y[1].month<<endl;

    Year y01,y02;                   //建立該型別變數
    y01.year=2004;                //使用成員運算子訪問其成員
    Year *p=&y02;                //通過指標使用間接成員運算子訪問其成員
    p->year=2005;

    const Year *q[2]={&y01,&y02};         //建立指標陣列
    cout<<q[0]->year<<endl;

    const Year **ppa=q;                   //建立指向以上陣列的指標
    cout<<(*ppa)->year<<endl;

    auto ppb=q;                            //與 Year **ppa=q 等價,auto提供方便推斷出ppb型別
    cout<<(*(ppb+1))->year<<endl;
    return 0;
}

陣列,vector和array比較

1, C++中內建陣列,簡單方便;陣列大小固定,速度較快;
   通用格式是:資料型別 陣列名[ 陣列大小 ];,
2, vector 是STL中的容器類,包含多種通用演算法;
   長度可變,使用靈活,但效率稍低;
   vector是使用 new 和 delete 來管理記憶體的,
3,array 陣列模板 ,在C++11中才支援;
   通用格式:array<型別名, 元素個數> 陣列名;
   注意,因為長度固定,這裡的元素個數不能是變數!長度固定,提供了更好、更安全的介面,執行效率和內建陣列相同,可以有效替代內建陣列

#include "stdafx.h"
#include <iostream>
#include <vector>
#include <array>
using namespace std;

int main(){ 
    int d[]={1,2,3,4,5,6,7};     //陣列
    string name[2];
    name[0]="Zhang";
    name[1]="Li";

    vector<double> v(2);         //vector
    v[0]=0.1;
    v[1]=0.2;


    array<double,2> a1={1.1,1.2}; //array
    array<double,2> a2;
    a2=a1;              //可以將一個array物件賦給另一個array物件;而對於陣列,必須逐元素複製資料

    cout<<d[1]<<" at "<<&d[1]<<endl;
    cout<<v[1]<<" at "<<&v[1]<<endl;
    cout<<a2[1]<<" at "<<&a2[1]<<endl;       //從地址可知,array物件和陣列儲存在相同的記憶體區域(即棧)中,而vector物件儲存在另一個區域(自由儲存區或堆)中
    return 0;
}