2017-01-06 19 views
2

我正在實現一個對小對象執行類型擦除的類,並且遇到了我不明白的分段錯誤。在類型擦除的小對象優化中調試崩潰

下面的程序:

#include <iostream> 
#include <type_traits> 

struct small_object 
{ 
    public: 
    template<class T> 
    small_object(const T& value) 
    { 
     new(&storage_) concrete<T>(value); 
    } 

    ~small_object() 
    { 
     get_abstract().~abstract(); 
    } 

    void print() const 
    { 
     // XXX crash here 
     get_abstract().print(); 
    } 

    private: 
    struct abstract 
    { 
     virtual ~abstract(){} 

     virtual void print() const = 0; 
    }; 

    template<class T> 
    struct concrete 
    { 
     concrete(const T& value) : value_(value) {} 

     void print() const 
     { 
     std::cout << value_ << std::endl; 
     } 

     T value_; 
    }; 

    abstract& get_abstract() 
    { 
     return *reinterpret_cast<abstract*>(&storage_); 
    } 

    const abstract& get_abstract() const 
    { 
     return *reinterpret_cast<const abstract*>(&storage_); 
    } 

    typename std::aligned_storage<4 * sizeof(void*)> storage_; 
}; 

int main() 
{ 
    small_object object(13); 

    // XXX i expect this line to print '13' to the terminal but it crashes 
    object.print(); 

    return 0; 
} 

崩潰在由XXX所指示的線。

我認爲問題在於對.print()的虛擬調用沒有被正確動態調度,但我不明白爲什麼。

任何人都可以告訴我缺少什麼?

回答

4

您沒有從abstract派生concrete<T>,因此在使用佈局new構造對象時沒有創建vtable。因此,當您嘗試調用虛函數時,它將失敗;在這個例子中,concrete<T>abstract實際上是完全不相關的類型。

如果您使用C++ 11或更新版本來允許編譯器在這種情況下生成錯誤,那麼我會推薦使用override關鍵字。

2
std::aligned_storage<4 * sizeof(void*)> storage_; 

這將創建一個字節的存儲

template參數不設置聲明對象的大小,而是設置可以在此類型的適當大小的數組中分配的對象的大小。因此,你需要

std::aligned_storage<4 * sizeof(void*)> storage_[4 * sizeof(void*)]; 

GCC 6.2.0提醒你一下:

警告:placement new型 'std::aligned_storage<32ul>' 的區域構造型 'small_object::concrete<int>' 的對象和大小 '16'和尺寸 '1'[-Wplacement全新=]

(您仍然需要從abstract獲得concrete)。