2016-04-06 71 views
1

我在討論中被問到在C++類中添加虛擬函數有什麼缺點。我說過一個缺點是類的對象有一個指向它的虛擬表的指針,對於一個小的C++類,它在64位平臺上增加了8個字節的大小。如果創建數百萬個這樣的類的實例會增加程序的內存消耗。確定,但爲什麼實際上在C++中沒有像虛擬表的小指針或指向虛擬表的小指針或指向虛擬表的小型指針這樣的事情。有些事情是這樣的:具有虛擬功能的緊湊型課程

class [[compact]] base { 
    ~base(){} 
    virtual f() = 0; 
}; 

class [[tiny]] another_base { 
    ~base(){} 
    virtual g() = 0; 
}; 

class [[small]] yet_another_base { 
    ~base(){} 
    virtual h() = 0; 
}; 

class child : public base { 
    virtual f(); 
}; 

class user_type : public another_base { 
    ~base(){} 
    virtual g(); 
}; 

想象一下,我要創造大量的user_type實例(其實我曾經有過一個真正的程序這種情況)。默認情況下,編譯器創建大小爲8(64位)的user_type實例。但是[[tiny]]屬性只有它可能只有1個字節,[[compact]]只有4個字節。

此功能是否可用?如果不是這樣,我覺得可以實現它。就像在程序中隱藏tiny_vptrcompact_vptr,並且當需要找到一個指向vtable的實際指針時,只需向它們添加第一個字節或前四個字節即可。所以在一個程序中,它只允許有256個[[tiny class]]或65000個[[small]]類。它就像是最高速度和節省內存之間的選擇。

+1

如果實現它,只會在派生類的第一個成員是1字節對齊的情況下(否則會在vtable指針和第一個成員之間填充字節)僅節省內存。我想你可以強制派生類被緊密打包,所以不會有填充字節,但是你只是進入了奇怪的特定於領域的領域。 – Cornstalks

+1

虛擬函數的真正危害並不是因爲vptr,而是因爲它們的不可插入性。 – SergeyA

+0

@SergeyA:OP正在詢問有關保存內存的問題。不可內聯性不會真的影響到這一點。 – Cornstalks

回答

3

沒有這樣的功能。

現在,C++的vtable特性(至少是你使用的部分)可以用C或C++來模擬,只需要一些語法糖。

struct my_vtable { 
    void(*dtor)(void*) = 0; 
    void(*print)(void*) = 0; 
    void(*add)(void*, int) = 0; 
}; 

struct my_interface { 
    my_vtable const* vtable = 0; 
    ~my_interface() { vtable->dtor(this); } 
    void print() { vtable->print(this); } 
    void add(int x) { vtable->add(this, x); } 
}; 

等。這需要更多的工作來獲得構造函數和析構函數級聯工作,和虛擬繼承是有點莽漢,並建立從多重繼承的複合虛函數表還正常工作(尤其是如果你想讓它清潔)。

但你有工具。

使用這些工具,您可以實現您的[[tiny]] vtable功能。

現在,如果您關心此問題,更好的方法可能是使用代理對象,您可以在任何狀態下使用任意函數來查找表。您將狀態存儲在實例中,並將表存儲在別處。

C++的對象系統是而不是所有那些靈活。它實現了一種安排繼承和麪向對象的方式。從佈局變更到功能變更(例如,C++不支持爲類實例構建一次性vtable或以其他方式動態創建新類型),還有許多其他功能都很有用。

將C++繼承系統視爲編寫OO代碼的許多方法之一。知道你應該有一個很好的理由擺脫它,並儘可能使代碼儘可能乾淨,但不要因爲你有繼承關係而不願意使用C++繼承。