1. 程式人生 > >C++PrimerPlus知識點小結

C++PrimerPlus知識點小結

  1. C++融合了3種不同的程式設計方式:C語言代表的過程性語言,C++在C語言基礎上新增的類代表的面嚮物件語言(oop),C++模板支援的泛型程式設計。
  2. C++11初始化方式
     //大括號初始化,等號可以使用,也可以不使用
    int emus{7};
    int rheas={12};
    //大括號內不包含任何東西,這種情況下變數將被初始化為零
    int rocs={};
    int psychics{};
  1. C++儲存整型的方式類似一個圈,負數存的是補碼。可以手動模擬一下。
  2. 第一位為1-9,則基數為10(十進位制),第一位為0,則基數為8(8進位制),前兩位為0x或0X,則基數為16
  3. cout的一些特殊特性
    /*
    cout提供了控制符dec,hex,oct分別用於指示cout以十進位制,
    十六進位制和八進位制格式顯示整數。
    */
    int tmp=42;
    cout<<tmp<<endl;
    cout<<hex;
    cout<<tmp<<endl;
    cout<<oct;
    cout<<tmp<<endl;
  1. C++11強制型別轉換運算子static_cast < typeName>(value)
    //使用要求更為嚴格,雖然不知道有什麼用。。
    char c='c';
    cout<<static_cast<int>(c)<<endl;
  1. cin.get() cin會留下換行符 cin.getline 不會留下換行符
  2. C++11新增的另一種型別:原始(raw)字串
    cout<<R"(Jim "King" Tutt uses "\n" instead of endl.)"<<'\n';
    //輸出 Jim "King" Tutt uses \n instead of endl.
  1. C++結構體種的位欄位
    C++允許指定佔用特定位數的結構成員,這使得建立與某個硬體裝置上的暫存器對應的資料結構非常方便。欄位的型別應為整型或者列舉,接下來是冒號,冒號後面跟著一個數字,它制定了位數。可以使用沒有名稱的欄位提供間距。每個成員都被稱為位欄位。
    struct torgle_register
    {
        unsigned int SN : 4;
        unsigned int : 4;
        bool goodIn : 1;
        bool goodTorgle : 1;
    };
    torgle_register tr = {14,true,false};
    if(tr.goodIn)puts("Yes");
  1. 共用體
    結構體可以同時儲存int,long和double,共用體只能儲存int,longdouble
    union one4all
    {
        int int_val;
        long long_val;
        double double_val;
    };
    one4all pail;
    pail.int_val=15;
    cout<<pail.int_val<<endl;
    pail.double_val=1.38;
    cout<<pail.double_val<<endl;
    cout<<pail.int_val<<endl;
    /*
    輸出
    15
    1.38
    -515396076
    */

由於共用體每次只能儲存一個值,因此它必須有足夠的空間來儲存最大的成員,所以,共用體的長度為其最大的成員的長度。
匿名共用體:

    struct widget
    {
        char brand[20];
        int type;
        union
        {
            long id_num;
            char id_char[20];
        };
    };
    widget prize;
    cin>>prize.type;
    if(prize.type==1)cin>>prize.id_num;
    else cin>>prize.id_char;

共用體常用於(但並非只能用於)節省記憶體。C++可用於嵌入式系統程式設計,如控制烤箱,MP3播放器或火星漫步者的處理器。對於這些應用程式來說,記憶體可能十分寶貴。另外,共用體常用於操作作業系統資料結構或硬體資料結構。

  1. sizeof運算子和指標,陣列之間的關係。
    char *p="abc";
    cout<<sizeof(p)<<endl;//指標長度,一律返回4
    cout<<sizeof(*p)<<endl;//字元長度,1個位元組
    char a[]="abcdef";
    cout<<sizeof(a)<<endl;//陣列長度 7
    cout<<sizeof(&a)<<endl;//指標長度,一律返回4
    cout<<sizeof(*a)<<endl;//字元長度,1個位元組
  1. 型別組合(P118)
    struct student
    {
        int year;
    };
    //變數
    student a1,a2,a3;
    a1.year=2003;
    a2.year=2005;
    //陣列
    student t[3];
    t[0].year=2003;
    (t+1)->year=2004;
    //指標陣列
    const student *b[3]={&a1,&a2,&a3};
    cout<<b[0]->year<<endl;
    //陣列指標
    const student **c=b;
    cout<<(*c)->year<<endl;
    cout<<c[0]->year<<endl;
  1. 基於範圍的for迴圈(C++11)(P152)
    C++新增一種迴圈,基於範圍(range-based)的for迴圈。
    double prices[5]={4.99,1.99,6.87,7.99,8.49};
    for(double x:prices)
        cout<<x<<endl;
    //要修改陣列的元素,需要使用不同的迴圈變數語法
    for(double &x:prices)
        x=x*0.8;
    //還可以使用基於範圍的for迴圈和初始化列表
    for(int x:{3,5,2,8,6})
        cout<<x<<' ';
    cout<<endl;
  1. 檔案尾條件(P155)
    輸入來自於檔案時,使用一些特殊符號來表示結束有時會很難令人滿意,這時我們可以通過檢測檔案尾(EOF)。
    如果輸入來自於鍵盤呢,很多作業系統都允許通過鍵盤來模擬檔案尾條件。在Unix中,可以在行首按下Ctrl+D來實現。在Windows命令提示符模式下,可以在任意位置按Ctr+Z和Enter。
    如果程式設計環境檢測到EOF後,cin將兩位(eofbit和failbit)都置為1.可以通過成員函式eof()來檢視eofbit是否被設定;如果監測到EOF,則cin.eof()將返回true,否則返回false。同樣,如果eofbit或failbit被置為1,則fail()成員函式返回true,否則返回false。
    cin.fail()比cin.eof()更通用,因為它可以檢測到其他失敗原因,如磁碟故障。
  2. cin.get()有兩種用法,一種ch=cin.get(),一種cin.get(ch)。
  3. 寫入及讀取檔案
	//寫入
	ofstream outFile;
    ofstream fout;
    outFile.open("fish.txt");
    char s[30];
    scanf("%s",s);
    fout.open(s);
    outFile<<"hello world"<<endl;
    outFile.close();
    fout.close();
    //讀取
    ifstream inFile;
    //ifstream fin;
    inFile.open("fish.txt");
    if(!inFile.is_open())
    {
        puts("Error");
        exit(EXIT_FAILURE);
    }
    char s[30];
    while(inFile>>s)
    {
        cout<<s<<endl;
    }
    inFile.close();
    //fin.close();
    /*
    fish.txt
    hello world
    */
  1. 使用陣列區間的函式(P220)
    迭代器原型
int sum_arr(const int *begin,const int *end)
{
    int tot=0;
    for(const int *pt=begin;pt!=end;pt++)
        tot=tot+*pt;
    return tot;
}
int main()
{
    sum[0]=1,sum[1]=2;sum[2]=3;
    cout<<sum_arr(sum,sum+3);
}
  1. 指標與const(P222)
    可以用兩種不同的方式將const關鍵字用於指標,第一種方法是讓指標指向一個常量物件,這樣可以防止使用指標來修改所指向的值,第二種方法是將指標本身宣告為常量,這樣可以防止改變指標指向的位置。下面來看細節。
	int age=30;
    const int *pt=&age;
    *pt += 1;//invalid
    const int age2=30;
    int *p2=&age2;//invalid

pt的什麼並不意味著指向的值是一個常量,而只是意味著對pt而言,這個值是一個常量。我們可以通過age變數來修改age的值,但不能用pt指標來修改它。
第二種也不行。
如果指標指向指標,情況更復雜。
假設涉及的是一級間接關係,則將非const指標賦給const指標是可以的。

    int age=39;
    int *pd=&age;
    const int *pt=pd; 

但進入兩級間接關係時,與一級間接關係一樣將const和非const混合的指標賦值方式將不再安全,因此是不行的。如果允許這樣做,則可以編寫這樣的程式碼:

    const int **pp2;
    int *p1;
    const int n=13;
    pp2=&p1;
    *pp2=&n;
    *p1=10;

上述程式碼將非const地址(&p1)賦給了const指標pp2,因此可以使用p1來修改const資料。因此,這樣是不行的。
如果資料型別本身並不是指標,則可以將const資料或非const資料的地址賦給指向const的指標,但只能將非const資料的地址賦給非const指標。

  1. 函式指標
    宣告時函式指標的返回型別以及引數列表和函式必須一樣。
double pam(int);
double (*pf)(int);
int main()
{
    pf=pam;
    double x=pam(4);
    double y=(*pf)(5);
    cout<<y<<endl;
    y=pf(5);//這樣寫也行
    cout<<y<<endl;
}
double pam(int a)
{
    return 1.0*a*a;
}

深入探討函式指標
假如現在有下面一些函式的原型,他們的引數列表和返回值型別相同:

const double *f1(const double ar[],int n);
const double *f2(const double [],int n);
const double *f3(const double *,int);

接下來,假設要宣告一個指標,它可指向這三個函式之一。假定該指標名為p1:

    const double *(*p1)(const double *,int);
    p1=f1;
    //還可以這樣寫
    auto p2=f2;

現在來看下面的語句:

	cout<<p1(a,3)<<' '<<*(p1(a,3))<<endl;
    cout<<p2(a,3)<<' '<<*(p2(a,3))<<endl;
	//前面返回的是地址,後面返回的是值

如果有一個函式指標陣列,包含這三個函式,該怎麼宣告呢?

const double * (*pa[3])(const double *,int)={f1,f2,f3}

這裡要說明一點,運算子[]的優先順序高於*,其他的可以自己理解。
如果要宣告一個函式指標陣列指標,用來指向pa陣列,該怎麼宣告呢?

//第一種
auto pc=&pa;
//第二種 pd是一個指標,它指向一個包含三個元素的陣列。	
const double *(*(*pd)[3])(const double *,int)=&pa;

(有點沒弄清楚)這樣的宣告方式應該是陣列指標的宣告。
要呼叫函式,需認識到這一點,既然pd指向陣列,那麼*pd就是陣列,而(*pd)[i]就是陣列中的元素,即函式指標。因此,較簡單的函式呼叫是(*pd)[i](av,3),而 ( p d ) [ i ] ( a v , 3 ) \ast(\ast pd)[i](av,3) 是返回的指標指向的值。也可以使用第二種使用指標呼叫函式的語法,使用 ( ( p d ) [ i ] ) ( a v , 3 ) (\ast (\ast pd)[i])(av,3) 來呼叫函式,而 ( ( p d ) [ i ] ) ( a v , 3 ) \ast (\ast (\ast pd)[i])(av,3) 是指向的double的值。
請注意pa(它是陣列名,表示地址)和&pa之間的差別。pa都是陣列第一個元素的地址,即&pa[0]。因此,它是單個指標的地址。但&pa是整個陣列(即三個指標塊)的地址。從數字上說,pa和&pa的值相同,但它們的型別不同。一種差別是,pa+1為陣列中下一個元素的地址,而&pa+1為陣列pa後面一個12位元組記憶體塊的地址(假定一個地址為4個位元組)。另一個差別是,要得到第一個元素的值,只需對pa解除一次引用,但需要對&pa解除兩次引用:
&amp; p a = = p a = = p a [ 0 ] ** \&amp;pa==*pa==pa[0]
使用typedef 進行簡化
typedef 放在型別宣告前,在變數的位置填上新型別的名字,這樣新型別就可以當舊型別用,換句話說,宣告變數時,在句首加上typedef,就相當於聲明瞭新型別。

typedef const double *(*p_fun)(const double *,int);
p_fun p1=f1;
p_fun pa[3]={f1,f2,f3};
p_fun (*pd)[3]=&pa;
  1. C++提供了許多新的函式特性,包括行內函數,按引用傳遞變數,預設的引數值,函式過載(多型)以及模板函式。

  2. 行內函數
    行內函數的編譯程式碼與其他的程式程式碼“內聯”到一塊,對於內聯程式碼,程式無需跳到另一個位置處執行程式碼,再跳回來。因此,行內函數的執行速度比常規函式稍快,但代價是需要佔用更多記憶體。
    應有選擇的使用行內函數。如果執行函式程式碼的時間比處理函式呼叫機制的時間長,則節省的時間只佔一小部分。如果程式碼執行時間很短,則內聯呼叫就可以節省非內聯呼叫使用的大部分時間。

  3. 臨時變數,引用引數和const(P262)
    如果引用引數是const,則編譯器將在下面兩種情況下生成臨時變數:

    • 實參的型別正確,但不是左值。
    • 實參的型別不正確,但可以轉換成正確的型別。

    左值:左值引數是可被引用的資料物件,例如,變數,陣列元素,結構成員,引用和解除引用的指標都是左值。非左值包括字面常量(用引號括起的字串除外,它們由其地址表示)和包含多項的表示式。在C語言中,左值最初指的是可出現在賦值語句左邊的實體,但這是引入關鍵字const之前的情況。現在,常規變數和const變數都可視為左值,因為可通過地址訪問他們。但常規變數屬於可修改的左值,而const變數屬於不可修改的左值。
    簡而言之,如果接受引用引數的函式的意圖是修改作為引數傳遞的變數,則建立臨時變數將阻止這種意圖的實現。解決方法是,禁止這麼做。現在C++標準正是這樣。
    如果只是使用傳遞的值,而不是修改它們,因此臨時變數不會造成任何不利的影響,反而會使函式在可處理的引數種類方面更通用。

  4. 繼承的一個特徵是,派生類繼承了基類的方法(如ofstream繼承了ostream,ofstream可以使用ostream的一些方法),另一個特徵是,基類引用可以指向派生類物件,而無需進行強制型別轉換。

  5. C++如何跟蹤每一個過載函式呢?C++編譯器將執行一些神奇的操作-名稱修飾或名稱矯正。它會根據函式原型中指定的形參型別對每個函式名進行加密。

  6. 函式模板

  7. 記憶體模型和名稱空間

  8. 物件和類