C++學習問題

[TOC]

@(學習)[C++]

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成員進行初始化
“`cpp
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;
}
##7,...

##8,this
類中的成員函數,都有一個附件的隱含實參,該實參(**this**)就是一個指向該類對象的指針。
##9,#include xx.h文件和xx有何區別

##10,訪問權限
###三種訪問權限
* public 可以被任意實體訪問
* protected 只允許子類和本類成員函數訪問
* private 只允許本類成員函數訪問

### 三種繼承方式 
* public繼承 不改變基類成員的訪問權限
* protected繼承 使得基類中public變成protected,其他權限不變。
* private繼承 使得基類中所有成員權限變成private
```cpp
    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 成員函數。

分享