1. 程式人生 > >C++基礎知識 基類指針、虛函數、多態性、純虛函數、虛析構

C++基礎知識 基類指針、虛函數、多態性、純虛函數、虛析構

這就是 nbsp rtu 可能 就會 delete 繼承 都是 del

一、基類指針、派生類指針

父類指針可以new一個子類對象

二、虛函數

有沒有一個解決方法,使我們只定義一個對象指針,就可以調用父類,以及各個子類的同名函數?

有解決方案,這個對象指針必須是一個父類類型,我們如果想通過一個父類指針調用父類、子類中的同名函數的話,這個函數是有要求的;

在父類中,eat函數聲明之前必須要加virtual聲明eat()函數為虛函數。

一旦某個函數被聲明為虛函數,那麽所有派生類(子類)中eat()函數都是虛函數。

為了避免你在子類中寫錯虛函數,在C++11中,你可以在函數聲明中增加一個override關鍵字,這個關鍵字用在子類中,而且是虛函數專用。

override就是用來說明派生類中的虛函數,你用了這個關鍵字之後,編譯器就會認為你這個

eat是覆蓋了父類中的同名函數(只有虛函數才存在子類可以覆蓋父類中同名函數的問題),那麽編譯器就會在父類中找同名的虛函數,如果沒找到,編譯器就會報錯,如果你不小心在子類中把虛函數寫錯了名字,寫錯了參數,編譯器能夠幫你進行糾錯。

final也是虛函數專用,用在父類中,如果我們在父類的函數聲明中加了final,那麽任何嘗試覆蓋在函數的操作都會引發錯誤。

調用虛函數執行的是“動態綁定”。動態表示我們程序運行的時候才能知道調用了那個子類的中的eat()虛函數。

動態的綁定到Men上去,還是Women上去,取決於newMen還是Women

動態綁定:運行的時候才決定你的

phuman對象綁定到那個eat()函數上運行。

三、多態性

多態性只是針對虛函數來說的;

多態性:體現在具有繼承關系的父類和子類之間,子類重新定義(重寫)父類的成員函數eat(),同時父類把這個eat()函數聲明為virtual虛函數;

通過父類的指針,只有到了程序運行時期,找到動態綁定到父類指針上的對象,這個對象有可能是某個子類對象,也可能是父類對象;

然後系統內部實際上是要查找一個虛函數表,找到函數eat()的入口地址,從而調用父類或子類的eat()函數,這就是運行時的多態性。

四、純虛函數

純虛函數是在基類中聲明的函數,但是他在基類中沒有定義,但是要求任何派生類都要定義該虛函數自己的實現方法;

基類中實現純虛函數的方法使在函數原型後面增加 =0;

一旦一個類中又純虛函數,那麽你就不能生成這個類的對象了;

抽象類不能用來生成對象,主要目的是用來同意管理子類對象;

1)純虛函數的類叫做抽象類,不能用來生成該類對象,主要用於當做基類來生成子類用的;

2)子類必須要實現該基類中定義純虛函數;

五、基類的析構函數一般寫成虛函數(虛析構函數)

用基類指針new子類的對象,在delete的時候,系統不會調用派生類的析構函數,存在問題;

解決方案:將基類的析構函數聲明為虛析構函數;

public繼承中,基類對派生類及其對象的操作,只能影響那些從基類繼承下來的成員,如果想要用基類對非繼承的成員進行操作,則要把基類的這個函數定義為虛函數,析構函數也為虛函數,基類中的析構函數的虛屬性也會被派生類繼承,即派生類的析構函數也為虛函數。

Human這個類中的析構函數就要聲明為virtual的,也就是說C++11中為了獲得運行時多態,所調用的成員必須是virtual的。

如果一個類想要做基類,我們必須將類的析構函數聲明為virtual虛函數;

只要基類中的析構函數為虛函數,就能保證我們delete基類指針時能夠運行正確,不會出現內存泄漏。

虛函數會增加內存開銷,類裏面定義虛函數,編譯器就會給這個類增加虛函數表,在這個表裏存放虛函數的指針。

本節案例:

// Human.h
// 頭文件防衛式聲明
#ifndef __HUMAN__
#define __HUMAN__

#include "stdafx.h"
class Human
{
public:
    Human();
    virtual ~Human();

public:
    virtual void eat();
    virtual void eat2() = 0;
};

#endif

// Human.cpp
#include "stdafx.h"
#include "Human.h"
#include <iostream>

Human::Human()
{
    std::cout << "調用了Human::Human()" << std::endl;
}

void Human::eat()
{
    std::cout << "人類喜歡吃各種美食" << std::endl;
}

Human::~Human()
{
    std::cout << "調用了Human::~Human()" << std::endl;
}

// Men.h
#ifndef __MEN__
#define  __MEN__

#include "stdafx.h"
#include "Human.h"

class Men : public Human
{
public:
    Men();
    ~Men();
public:
    virtual void eat() override;
    virtual void eat2();
};

#endif

// Men.cpp
#include "stdafx.h"
#include "Men.h"
#include <iostream>

void Men::eat()
{
    std::cout << "男人喜歡吃米飯" << std::endl;
}

void  Men::eat2()
{
    std::cout << "男人喜歡吃米飯2" << std::endl;
}

Men::Men()
{
    std::cout << "調用了Men::Men()" << std::endl;
}

Men::~Men()
{
    std::cout << "調用了Men::~Men()" << std::endl;
}

// Women.h
#ifndef __WOMEN__
#define  __WOMEN__
#include "stdafx.h"
#include "Human.h"
class Women : public Human
{
public:
    Women();
    ~Women();

public:
    virtual void eat() override;
    virtual void eat2() override;
};

#endif

// Women.cpp
#include "stdafx.h"
#include "Women.h"
#include <iostream>

Women::Women()
{
    std::cout << "調用了Women::Women()" << std::endl;
}

Women::~Women()
{
    std::cout << "調用了Women::~Women()" << std::endl;
}

void Women::eat()
{
    std::cout << "女人喜歡吃面食" << std::endl;
}

void Women::eat2()
{
    std::cout << "女人喜歡吃面食2" << std::endl;
}

// main.cpp
// Project3.cpp : 定義控制臺應用程序的入口點。
//

#include "stdafx.h"
#include <iostream>
#include "Human.h"
#include "Men.h"
#include "Women.h"

using namespace std;

int main()
{
    Human *phuman = new Men;
    phuman->eat();  // 男人喜歡吃米飯
    delete phuman;

    phuman = new Women;
    phuman->eat(); // 女人喜歡吃面食
    delete phuman;

    //phuman = new Human;
    //phuman->eat(); // 人類喜歡吃各種美食
    //delete phuman;
    phuman = new Men;
    phuman->eat2();  // 男人喜歡吃米飯
    delete phuman;

    phuman = new Women;
    phuman->eat2(); // 女人喜歡吃面食
    delete phuman;

    //Men men;
    Men *pmen = new Men;
    delete pmen;

    Human *phuman1 = new Men;
    delete phuman1;  // 沒有執行子類的析構函數

    return 0;
}

C++基礎知識 基類指針、虛函數、多態性、純虛函數、虛析構