[C++] 為什麼我們在C ++中沒有虛擬構造函數?


Answers

虛函數基本上提供多態行為。 也就是說,當您使用動態類型與引用它的靜態(編譯時)類型不同的對象時,它提供的行為適合實際類型的對象而不是對象的靜態類型。

現在嘗試將這種行為應用於構造函數。 當你構造一個對象時,靜態類型總是與實際對像類型相同,因為:

要構造一個對象,構造函數需要它要創建的對象的確切類型[...]此外,您不能有指向構造函數的指針

(Bjarne Stroustup(P424 The C ++ Programming Language SE))

Question

為什麼C ++沒有虛擬構造函數?




我們不能簡單地說它就像..我們不能繼承構造函數。 所以說虛擬是沒有意義的,因為虛擬提供了多態性。




C ++虛擬構造函數是不可能的。例如,你不能將一個構造函數標記為virtual.Try這個代碼

#include<iostream.h>
using namespace std;
class aClass
{
    public:
        virtual aClass()
        {   
        }  
};
int main()
{
    aClass a; 
}

它會導致錯誤。此代碼嘗試將構造函數聲明為虛擬。 現在讓我們試著了解為什麼我們使用虛擬關鍵字。 虛擬關鍵字用於提供運行時多態性。 例如試試這個代碼。

#include<iostream.h>
using namespace std;
class aClass
{
    public:
        aClass()
        {
            cout<<"aClass contructor\n";
        }
        ~aClass()
        {
            cout<<"aClass destructor\n";
        }

};
class anotherClass:public aClass
{

    public:
        anotherClass()
        {
            cout<<"anotherClass Constructor\n";
        }
        ~anotherClass()
        {
            cout<<"anotherClass destructor\n";
        }

};
int main()
{
    aClass* a;
    a=new anotherClass;
    delete a;   
    getchar(); 
}

在主a=new anotherClass; 在聲明為類的類型的指針中為anotherClass分配內存。這會導致構造函數(在aClassanotherClass )自動調用。因此,我們不需要將構造函數標記為虛函數。因為創建對象時必須遵循創造的鏈條(即首先是基礎,然後是派生類)。 但是,當我們嘗試刪除一個delete a; 它會導致只調用基本的析構函數。所以我們必須使用virtual關鍵字來處理析構函數。 所以虛擬構造函數是不可能的,但虛擬析構函數是。謝謝




你可以在@stefan的答案中找到一個例子,以及為什麼它不被允許的技術原因。 根據我的觀點,現在對這個問題的邏輯回答是:

虛擬關鍵字的主要用途是在我們不知道基類指針將指向什麼類型的對象時啟用多態行為。

但想一想這是更原始的方式,因為使用虛擬功能你需要一個指針。 指針需要什麼? 一個指向的對象! (考慮正確執行程序的情況)

所以,我們基本上需要一個已經存在於內存某處的對象(我們不關心內存如何分配,它可能在編譯時或運行時),以便我們的指針可以正確指向該對象。

現在,想一下當被指定的類的對像被分配了一些內存時的情況 - >它的構造函數將在該實例本身被自動調用!

所以我們可以看到,我們實際上並不需要擔心構造函數是虛擬的,因為在任何情況下,如果您希望使用多態行為,我們的構造函數已經被執行,使得我們的對象可以使用!




C ++中的虛函數是運行時多態的實現,它們將執行函數重寫。 通常,當您需要動態行為時, virtual關鍵字在C ++中使用。 它只會在物體存在時才起作用。 而構造函數用於創建對象。 構造函數將在創建對象時調用。

因此,如果按照虛擬關鍵字定義將構造函數創建為virtual ,則應該使用現有對象,但是構造函數用於創建對象,因此這種情況將永遠不會存在。 所以你不應該使用構造函數作為虛擬。

所以,如果我們嘗試聲明虛擬構造函數編譯器拋出一個錯誤:

構造函數不能被聲明為虛擬的




我能想到的兩個理由:

技術原因

該對像只在構造函數結束後才存在。為了使用虛擬表分派構造函數,必須有一個存在虛擬表指針的現有對象,但是如果對象存在,則指向虛擬表的指針如何存在仍然不存在? :)

邏輯的原因

當你想聲明一個有點多態的行為時,你使用virtual關鍵字。 但是構造函數沒有多態性,C ++中的構造函數的作用就是簡單地將對像數據放在內存中。 由於虛擬表(通常是多態)都是關於多態行為,而不是多態數據,所以聲明一個虛擬構造函數沒有意義。




拋開語義原因,在構造對象之後才會有vtable,從而使虛擬指定無用。




儘管虛擬構造函數的概念並不適合,因為對像類型是創建對象的先決條件,但它並沒有完全超越規則。

GOF的“工廠方法”設計模式利用了虛擬構造函數的“概念”,這在特定的設計情況下很適用。




如果你從邏輯上思考構造函數是如何工作的以及C ++中虛擬函數的含義/用法是什麼,那麼你將會意識到在C ++中虛擬構造函數將毫無意義。 在C ++中聲明虛擬內容意味著它可以被當前類的子類覆蓋,但是在創建對象時調用構造函數,那時你不能創建類的子類,你必須創建類,所以永遠不需要聲明構造函數虛擬。

另一個原因是,構造函數與它的類名具有相同的名稱,並且如果我們將構造函數聲明為虛擬的,那​​麼它應該在其派生類中重新定義為具有相同名稱,但不能具有兩個類的相同名稱。 所以不可能有虛擬構造函數。