c++之析構函數是否設置為虛函數


1.首先說一下,析構順序

​ 派生類--> 成員類 --> 基類

2.為什么需要把基類設置為虛析構

​ 因為多態
​ 在c++中,可以使用父類指針指向子類,產生多態行為
代碼

#include <iostream>
class TestFather{
public:
    ~TestFather() {
        std::cout << "~TestFather()" << std::endl;
    }
};
class TestChild : public TestFather {
public:
    ~TestChild() {
        std::cout << "~TestChild()" << std::endl;
    }
};


int main() {
    TestFather* p = new TestChild();
    delete p;
}

//////////////////////////
執行結果  
~TestFather()

原因就是,你直接給編譯器一個TestFather指針,delete的時候,編譯器一看這不就是TestFather,直接調用TestFather析構函數
換成虛析構呢
代碼

#include <iostream>
class TestFather{
public:
    virtual ~TestFather() {
        std::cout << "~TestFather()" << std::endl;
    }
};
class TestChild : public TestFather {
public:
    ~TestChild() {
        std::cout << "~TestChild()" << std::endl;
    }
};


int main() {
    TestFather* p = new TestChild();
    delete p;
}

///////
執行結果
~TestChild()
~TestFather()

正確了,在delete的時候,編譯器會先看TestFather的析構函數是不是虛函數,如果是的話才會產生正常的析構順序行為,派生類-->成員類-->基類

3. 虛析構函數的本質

虛析構其實也就是虛函數加上析構函數,本質就是會維護一個虛表和指向虛表的指針,在上面代碼中TestFather這里面的虛表就只有~TestFather()這一個函數,使用虛函數代表會增加一個指針的內存開銷

4. 默認的析構函數

當我們不定義虛構函數的時候,編譯器會默認生成一個什么都不做的析構函數,但是注意了默認生成的析構函數就是普通函數不是虛函數!!!因為虛函數會帶來額外開銷,c++追求的是速度

5. 純虛構析構函數

就是純虛函數加上析構函數,一般我們把函數設置純虛函數都是不想這個類實例化,抽象出來的頂層父類,並且這個純虛函數不能實現。但是在純虛析構這里不同
代碼

#include <iostream>
class TestFather{
public:
    virtual ~TestFather() = 0;
};
TestFather::~TestFather() {
    std::cout << "~TestFather()" << std::endl;
}

class TestChild : public TestFather {
public:
    ~TestChild() {
        std::cout << "~TestChild()" << std::endl;
    }
};


int main() {
    TestFather* p = new TestChild();
    delete p;
}

//結果和上面的相同

因為析構函數的調用順序是派生類 成員類 基類,就算你基類是純虛函數,編譯器還是會產生對他的調用,所以要保證為純虛析構提供函數體,如果你不做編譯器會自動加上。
這里的純虛實現要在外面實現,不能在類中 = 0之后直接實現,那樣直接違反了語法

6. 那么如果父類有純虛析構函數,子類繼承后,怎么定義子類實例呢?

​ 只要子類定義自己的虛函數就可以,不論繼承多少層,只要有一個子類定義自己的虛函數,這個子類之后的派生類就都可以定義實例了

7. 關於virtual的隱士傳播

在上面的代碼中我們基類設置為純虛函數的時候,這個virtual關鍵字會被一直繼承下去
代碼

#include <iostream>
class TestFather{
public:
    virtual ~TestFather() = 0;
};
TestFather :: ~TestFather() {
    std::cout << "~TestFather()" << std::endl;
}

class TestChild : public TestFather {
public:
    ~TestChild() {
        std::cout << "~TestChild()" << std::endl;
    }
};

class TestSun : public TestChild {
public:
    ~TestSun() {
        std::cout << "~TestSun()" << std::endl;
    }
};


int main() {
    TestFather* p = new TestSun();
    delete p;
}

結果:
~TestSun()
~TestChild()
~TestFather()

所以當基類是virtual函數,無論子類的相同函數加或者不加這個關鍵字都是virtual的,但是為了其他人看代碼方便,建議手動把從基類繼承的虛函數加上virtual

7. 總結:

​ 明確你的類會不會被繼承,當作基類使用,把類的析構函數都設置為虛函數和不設置為虛函數都是不好的
​ 1.如果你的類會被繼承,當作基類,那么一定要把基類析構函數設置為虛函數
​ 2.如果你的類不會被繼承,單純的類,那么不需要把析構函數設置為析構函數,因為會浪費空間,多一個虛表指針


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM