c++的沼澤之函數與虛函數

個人覺得c++繼承與多態這塊彷彿掉進了泥潭,深陷其中無法自拔。現將部分迷茫的問題總結如下:

1.析構函數爲什麼要聲明爲虛函數?

基類的析構函數需要聲明爲虛函數:
當派生類對象經由基類指針刪除時,而基類帶着一個non_virtual析構函數,實際執行時發生的是對象的派生類對象成員沒有被銷燬。即局部銷燬,會發生內存泄漏,故通常將基類析構函數聲明爲虛函數

代碼實現爲:

#include <iostream>
using namespace std;
#include <windows.h>

class A //基類 
{
public:
    A()//構造函數
        :_a(1)
    {
        cout << "A()" << endl;
    }

    ~A()//析構函數
    {
        cout << "~A()" << endl;
    }
private:
    int _a;
};

class B :public A//B繼承A
{
public:
    B()//構造函數
        :_b(2)
    {
        cout << "B()" << endl;
    }
    ~B()//析構函數
    {
        cout << "~B()" << endl;
    }
private:
    int _b;
};

int main()
{
    A *p = new B;//基類指針指向子類對象
    delete p;//刪除指針
    system("pause");
    return 0;
}

結果:
這裏寫圖片描述

由此可以發現最後只刪除了A類的對象,而沒有刪除B類的,造成了局部銷燬。
解決方案:將基類析構函數聲明爲virtual,此後刪除派生類對象就會銷燬整個對象,當然包括派生類成員。
解決部分代碼:

//同上面代碼區別是將A類的析構函數聲明爲虛函數
#include <iostream>
using namespace std;
#include <windows.h>

class A
{
public:
    A()
        :_a(1)
    {
        cout << "A()" << endl;
    }

    virtual ~A()//聲明爲virtual
    {
        cout << "~A()" << endl;
    }
private:
    int _a;
};

class B :public A
{
public:
    B()
        :_b(2)
    {
        cout << "B()" << endl;
    }
    ~B()
    {
        cout << "~B()" << endl;
    }
private:
    int _b;
};

int main()
{
    A *p = new B;
    delete p;
    system("pause");
    return 0;
}

結果爲:
這裏寫圖片描述
以上方法解決了局部銷燬問題。delete p時經編譯器特殊處理後調用destructor() +operator delete從而刪除指針p

注意:

析構函數經編譯器處理後並非~A(),而是destructor,因此會對基類的析構函數構成隱藏,再者析構函數不能被繼承。將基類的析構函數聲明爲虛函數後,子類對基類的析構函數構成重寫,根據對象的指向,調用相應的析構函數。

這裏寫圖片描述

2.不能被聲明爲虛函數的函數

1. 普通函數
普通函數只能被重載,不能被重寫,聲明爲虛函數無任何意義,因爲編譯器會在編譯時綁定函數。

2.構造函數
因爲構造函數本身就是爲了初始化對象成員。如果被聲明爲虛函數,就需要通過vtable虛基表調用,但此時對象還未實例化,找不到vtable,因此不能爲虛函數。
從實現上看,vtable實在構造函數調用後才建立的,故構造函數不能爲虛函數,同時編譯時會報錯。

3.內聯函數
內聯函數是在代碼中直接展開,減少調用函數棧的開銷,而虛函數是爲了繼承後對象能準確的執行自己的操作,而且內聯函數在編譯時展開,虛函數在運行時動態綁定。

4.靜態成員函數
靜態成員函數對於每個類來說只有一份,各個對象共享一份代碼,它的實現不是爲了構成多態,而是爲了處理靜態數據成員,沒有動態綁定的必要性。

5.友元函數
友元函數不能被繼承即若A爲B的友元函數,C是B的友元函數,則不能說C是A的友元函數。都沒有繼承特性,更談不上虛函數的說法了!

發佈了128 篇原創文章 · 獲贊 51 · 訪問量 4萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章