1. 程式人生 > >從C過度到C++,你必須要掌握的知識點

從C過度到C++,你必須要掌握的知識點

內容原創,未經本人同意請勿轉載。聯絡本人:[email protected]

1,virtual函式

函式之前加上virtual關鍵字就表示該函式為虛擬函式,在派生類中通過重寫該虛擬函式來實現對基類函式的覆蓋。

//基類中定義virtual函式
class base
{
public:
    virtual void fun() {cout<<"BASE";}
};
//派生類中覆蓋fun函式
class derived: base
{
public:
    //不管該函式之前是否新增virtual欄位,該函式都是虛擬函式
    void fun() {cout<<"DERIVED";}
};
/*多型使用*/
int main()
{
    base* d = new derived();
    /*類的多型,呼叫的是派生類中的fun函式*/
    d->fun();
}
//輸出 
DERIVED
//
void call_fun(base* b)
{
    //如果b是base類的例項,就呼叫base中的fun
    //如果b是derived類的例項,就呼叫derived中的fun
    b->fun();
}

為何”虛”—動態聯編

virtual函式用到了動態聯編推遲聯編的技術,virtual函式在編譯的時候是無法確定的,而是在執行的時候被確定的。
編譯器在發現類中有virtual函式的時候,就會為該類分配一個VTABLE函式指標陣列,這個數組裡存放了類中的所有虛擬函式。

一個類只有一個VTABLE,不管有多少個例項
派生類有各自的VTABLE
同一個虛擬函式在基類和派生類的VTABLE的相同位置
編譯器在編譯的時候為每個例項在記憶體中分配一個vptr欄位,該欄位指向本例項的VTABLE

    void call_fun(base* b)
    {
        //如果b是base類的例項,就呼叫base中的fun
//如果b是derived類的例項,就呼叫derived中的fun //編譯後該函式b->fun();變成 (base->vptr[1])(); //這樣根據傳遞進來的例項不同,就呼叫不同的函式。 }

純虛擬函式(interface類)

    class base
    {
    public:
        virtual fun() = 0; //0標誌一個虛擬函式為純虛擬函式  
    }

純虛擬函式表示該類是一個抽象類,無法被例項化,只能被派生類繼承覆蓋。用來規範派生類,這就是所謂的“介面”類,用來約束派生類需要實現這些函式。

2,命令空間

定義

    namespace ns1
    {
        int a;
        int b;
        class base{};
        void fun(){}
    }

分離式定義

one.h

    #ifndef TWO_H_
    #define TWO_H_
    namespace two
    {
        void say();
    }
    #endif

two.h

    #ifndef TWO_H_
    #define TWO_H_
    namespace two
    {
        void say();
    };
    #endif

one_two.cpp

    #include <iostream>
    #include "one.h"
    #include "two.h"
    void one::say()
    {
        cout<<"one say\r\n";
    }
    void two::say()
    {
        cout<<"two say\r\n";
    }
    //如果宣告的空間有類如何實現????

使用

若想使用某個識別符號,using 空間名::識別符號;
若想使用改namespace下的所有識別符號 using namespace 空間名;

    //方法1
    using namespace one;
    //方法2
    using one::say;

自定義空間名使用

    #include <iostream>
    #include <string>
    using namespace std;
    using namespace one;
    using namespace two;

    //全域性函式
    void say()
    {
        cout<<"global say\r\n";
    }
    int main()
    {
        //全域性函式,一定要加上::
        //否則會出現 錯誤:呼叫過載的‘say()’有歧義
        ::say();
        one::say();
        two::say();
    }

3,模板

模板可以實現邏輯相同,但是資料型別不同的程式碼複製。當用戶使用模板時,引數由編譯器決定,這很像巨集
模板分為函式模板類模板

函式模板

定義&使用

    template <型別引數表> 返回型別 函式名(形參列表) {函式實體}

    template <typename T> void print(const T& var)
    {
        cout<<var<<endl;
    }

    int main()
    {
        String a("hello template");
        int num = 123;
        print(a);
        print(num);
    }
    /**********多個引數***********/
    template <class T> T min(T ii, T jj, T kk)
    {
        T temp;
        if(ii < jj) temp = ii;
        else temp = jj;
        if(temp > kk) temp = kk;
        return temp;
    }
    int main()
    {
        int minNum = min(100, 30, 102);
        cout<<minNum<<endl;
        char minChar = min('z', 'a', 'h');
        cout<<minChar<<endl;
        return 0;
    }

類模板

4,操作符過載

4.1,怎麼定義該函式

使用operator xx(xx表示操作符,例如==,+,-,等

    class person{
    private:
        int age;
    public:
        person(int a):age(a){};
        inline operator == (const person& p) const;
    };

    inline person::operator==(const person& p) const
    {
        if(this->age == p.age)
        {
            return true;
        }
        else
        {
            return false;
        }
    }

    using namespace std;
    void main()
    {
        person p1(10);
        person p2(20);

        if(p1 == p2)
        {
            cout<<"the age equal"<<endl;
        }
        else
        {
            cout<<"the age different"<<endl;
        }
    }

4.2,為什麼要過載

對於系統的所有操作符,一般情況下,只支援基本資料型別和標準庫中提供的class,對於使用者自己定義的class,如果想支援基本操作,比如比較大小,判斷是否相等,等等,則需要使用者自己來定義關於這個操作符的具體實現。比如,判斷兩個人是否一樣大,我們預設的規則是按照其年齡來比較,所以,在設計person 這個class的時候,我們需要考慮操作符==,而且,根據剛才的分析,比較的依據應該是age。那麼為什麼叫過載呢?這是因為,在編譯器實現的時候,已經為我們提供了這個操作符的基本資料型別實現版本,但是現在他的運算元變成了使用者定義的資料型別class,所以,需要使用者自己來提供該引數版本的實現。

4.3,操作符過載為全域性函式

估計沒人會這麼使用

對於全域性的操作符過載函式,左運算元的引數必須被顯式的定義。

bool operator == (person const &p1, person const &p2);

如何決定使用全域性還是類操作符過載函式呢?

  • 1 左操作符和比較物件是不是同一個型別
  • 2 C++要求,=、[]、()、->、必須定義為類操作符過載函式

5,過載函式

5.1 什麼叫過載函式?

在同一個作用域內,可以有一組具有名字相同,引數不同的一組函式。過載函式用來命名一組功能相似的函式。

5.2 為什麼要用過載函式

  • 必須寫很多函式名,來完成功能相似的一組函式
  • 類建構函式,如果沒有過載。那如果要例項化不同的類是比較麻煩的。
  • 操作符過載本身也是函式過載,豐富了已有操作符的功能。

5.3 編譯器如何解決命名衝突

編譯器會把不同引數名字相同的函式,用新的函式名取代。恩,其實也就還是不同名字,但是寫起來方便很多

5.4 重寫函式(override)

子類重新定義父類中有相同名稱、相同引數虛擬函式

  • 被重新定義的函式不能為static函式
  • 重寫的函式一定要完全相同(包括返回值、引數)
  • 重寫的函式可以有不同的修飾符,例如在基類中是private,派生類可以寫成public、protected

5.5 重定義函式(redefining)

子類重新定義父類中具有相同名字的函式(引數列表可以不同)。

6,static類

6.1 static成員函式

static資料成員是儲存在程式的靜態儲存區,而並不是在棧空間上。獨立於任何類的物件。

注意:static成員函式
* 沒有this指標。因為static成員函式不是任何物件的組成部分
* 不能宣告為const型別,不能訪問非static成員,也不能訪問static const成員。

6.2 static成員

注意:在類中不能對static成員進行初始化

    class person{
    private:
        static int age;
        //static int age= 20; //錯誤:ISO C++ 不允許在類內初始化非常量靜態成員'person::age'
        static string name;
    public:
        void print()
        {
            cout<<"name: "<<name<<" age: "<<age<<endl;
        }
    }

    int person::age = 30;
    string person::name = "kevin";

    int main()
    {
        //int person::age = 20;  錯誤:對限定名‘person::age’的使用無效
        person p;
        p.print();
        return 0;
    }

內容原創,未經本人同意請勿轉載。聯絡本人:[email protected]

7,…

8,this

類中的成員函式,都有一個附件的隱含實參,該實參(this)就是一個指向該類物件的指標。

9,#include xx.h檔案和xx有何區別

10,訪問許可權

三種訪問許可權

  • public 可以被任意實體訪問
  • protected 只允許子類和本類成員函式訪問
  • private 只允許本類成員函式訪問

三種繼承方式

  • public繼承 不改變基類成員的訪問許可權
  • protected繼承 使得基類中public變成protected,其他許可權不變。
  • private繼承 使得基類中所有成員許可權變成private
    class base{};
    class deliverd : public base{}; //public繼承

11, 過載和覆蓋

12, new delete

13, const成員函式

若將成員成員函式宣告為const,則該函式不允許修改類的資料成員

1)const成員函式可以訪問非const物件的非const資料成員、const資料成員,也可以訪問const物件內的所有資料成員;
2)非const成員函式可以訪問非const物件的非const資料成員、const資料成員,但不可以訪問const物件的任意資料成員;
3)作為一種良好的程式設計風格,在宣告一個成員函式時,若該成員函式並不對資料成員進行修改操作,應儘可能將該成員函式宣告為const 成員函式。