close

由於第一份工作幾乎都在寫C++,對於一些C++的概念也來記錄一下吧

首先是我覺得滿重要的一個概念,就是virtual function

如果嫌太麻煩可以去看這個 https://openhome.cc/Gossip/CppGossip/VirtualFunction.html,我就是看這個看懂的XD

 

這邊假設一個情境吧,以動物來舉例好了

人是動物、鳥是動物、狗也是動物嘛,都是動物(廢話一堆XD)

今天我開了一間動物園,裡面有鳥、貓跟猴子三種動物

因為要讓他們好好相處(我覺得現實應該是經費不足之類的),我把他們都放在同一個區域裡面

有時候會訓練他們表演,或是餵牠們吃東西什麼的,不同的動物就會有不同的動作

 

...這跟virtual function有什麼關係

 

當然有關係,這邊來想想怎麼用程式來呈現上面那個情境

一開始提到動物嘛,所以我們就建一個class叫做animal,不管什麼動物都有三種動作:

class animal
{
public:
    animal();
    ~animal();

    virtual void eat() = 0;
    virtual void move() = 0;
    virtual void show() = 0;
};

這邊virtual void eat() = 0;是什麼意思呢?

這就是所謂的純虛擬函式,表示在這個類別裡是不實作的,在子代裡面才會去實作

而只要具有純虛擬函式的類別,又叫做抽象類別(Abstract class)或多型類別(Polymorphic class)

"動物"只是一個群族的統稱,並沒有任何一種動物叫做"動物"吧

所以多型類別也沒辦法被建構出來,只能當作其他類別的基底類別

另外子代如果沒有實作純虛擬函式的話也不能被建構,一定要實作純虛擬函式,這個類別才能被建構

 

接下來我們來建鳥類的class吧,因為鳥是動物嘛,我們就讓他繼承animal:

class bird : public animal
{
public:
    bird();
    ~bird();

    void eat();
    void move();
    void show();
};

// 以下實作他的一些function

void bird::eat()
{
    std::cout << "bird eat\n";
}

void bird::move()
{
    std::cout << "bird move\n";
}

void bird::show()
{
    std::cout << "bird show\n";
}

然後我們再來建貓的class,一樣是以animal當作基底:

class cat : public animal
{
public:
    cat();
    ~cat();

    void eat();
    void move();
    void show();
};

void cat::eat()
{
    std::cout << "cat eat\n";
}

void cat::move()
{
    std::cout << "cat move\n";
}

void cat::show()
{
    std::cout << "cat show\n";
}

 

然後我們可以來做個小實驗,假設底下這段code:

int main()
{
    bird *bird1 = new bird();
    cat *cat1 = new cat();

    bird1->eat();
    cat1->eat();


    return 0;
}

會輸出什麼呢,這個應該沒什麼疑慮吧XD

bird eat

cat eat

 

然後我們再來討論剛剛的情況,因為要讓他們好好相處,我把所有"動物"都養在同一個區域裡面

這就是多型的其中一個目的了,因為他們都是同一個基底類別,都是動物,所以他們可以被放在同一個結構中

std::vector<animal*> zoo;

所以就可以這麼做:

int main()
{
    std::vector<animal*> zoo;

    zoo.emplace_back(new bird());
    zoo.emplace_back(new cat());

    for (animal* an : zoo)
    {
        an->eat();
    }

    system("pause");
    return 0;
}

輸出結果會和上面一模一樣

也就是說當指標類型為animal的時候,在執行期時如果遇到要執行virtual function,會往子代去找到對應的function來執行

其實關鍵在於指標的型態

 

接下來來看一個錯誤使用的例子

前面有看到鳥類嘛,鳥可能會飛嘛

所以我們在bird裡面加一個function叫做fly():

void fly();

實作長底下這樣:

void bird::fly()
{
    std::cout << "bird fly\n";
}

然後因為鳥有很多種,有些鳥不會飛,像是企鵝

所以我們建一個類別叫做企鵝,裡面特別宣告和實作fly()這個function:

void fly();

void penguin::fly()
{
    std::cout << "can't fly\n";
}

然後我們可以注意一下,上面fly()前面是沒有virtual的,來執行以下程式碼:

bird* b1 = new bird();

penquin* p1 = new penquin();

b1->fly();

p1->fly();

結果會輸出:

bird fly

can't fly

這是正常的結果,這邊注意一下指標的型態

 

但前面說過因為企鵝就是鳥嘛,所以我們改一下指標的型態:

bird* b1 = new bird();

bird* p1 = new penquin();

b1->fly();

p1->fly();

這樣會輸出:

bird fly

bird fly

 

??????

充滿問號對吧,我明明只是改了penquin的指標改成bird,怎麼就不會去執行penquin的function了呢

所以前面說了關鍵是指標的型態,因為執行期時就是靠指標來找要執行哪個函數的

那如何讓他輸出正確的結果呢?

很簡單,就是在bird的fly()宣告前面加個virtual:

virtual void fly();

因為有了virtual關鍵字,在執行期的時候執行到這個函數就會往子代找對應型態的實作來執行

如果沒加的話,bird的指標就只會找到bird的實作而已,不會繼續往子代的實作找

 

那麼什麼時候加virtual呢,其實也很好判斷

如果你的子代在這個function裡的動作和母代不同,要另外實作的話,就可以加上virtual

就像幾乎所有的鳥都會飛,可能只有企鵝和鴕鳥不會飛,這種情況就可以加上virtual

 

 

這邊有看到幾篇寫得很不錯,值得參考參考:

這是講virtual function的:https://blog.csdn.net/ring0hx/article/details/1605254 

這是講C++11中final和override關鍵字的,可以在編譯期就發現上面誤用的情況: https://kheresy.wordpress.com/2014/10/03/override-and-final-in-cpp-11/

好啦大概就介紹到這~ 想寫好C++就要好好記住這些啊XD

arrow
arrow
    全站熱搜

    頁頁頁六滴 發表在 痞客邦 留言(0) 人氣()