2017-01-06 1 views
2

小さなオブジェクトの型削除を実行するクラスを実装していて、わからないセグメンテーションフォルトが発生しました。型の削除のための小さなオブジェクトの最適化でのクラッシュのデバッグ

以下のプログラム:XXXで示す線で

#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; 
} 

クラッシュ。

.print()への仮想呼び出しが動的に正しくディスパッチされないという問題があると思いますが、その理由はわかりません。

私は何が欠けているか誰にでも教えてください。

答えて

4

concrete<T>abstractから派生させていないので、配置newを使用してオブジェクトを構築すると、vtableが作成されません。したがって、仮想関数を呼び出そうとすると失敗します。 concrete<T>およびabstractは、この例では実際には完全に無関係のタイプです。

このような場合にコンパイラがエラーを生成するためには、C++ 11以降を使用している場合はoverrideキーワードを使用することをお勧めします。

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

これは、1バイトのストレージを作成します。

テンプレート引数は、宣言されたオブジェクトのサイズを設定するのではなく、このタイプの適切なサイズの配列に割り当てることができるオブジェクトのサイズを設定します。

警告:placement newは型 'std::aligned_storage<32ul>' の領域で 'small_object::concrete<int>' タイプのオブジェクトを構築し、サイズ '16' したがって、あなたは

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

GCC 6.2.0はこれについて警告を出しを必要としますそしてサイズ '1' [-Wplacement-新しい=]

(あなたはまだabstractからconcreteを導出する必要があります)。

関連する問題