2017-01-06 1 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

Вы не получили concrete<T> от abstract, поэтому при построении объекта с помощью размещения new не создается vtable. Поэтому, когда вы пытаетесь вызвать виртуальную функцию, она не работает; concrete<T> и abstract на самом деле абсолютно несвязанные типы в этом примере.

Я бы порекомендовал использовать ключевое слово override, если вы используете C++ 11 или новее, чтобы компилятор мог генерировать ошибку в таких случаях.

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

Это создает для хранения одного байта.

Аргумент шаблона не задает размер объявленного объекта, а скорее размер объекта, который может быть выделен в массиве подходящего размера этого типа. Следовательно, вам нужно

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

GCC 6.2.0 предупреждает вас об этом:

предупреждение: placement new построения объекта типа 'small_object::concrete<int>' и размер '16' в области типа 'std::aligned_storage<32ul>' и размер '1' [-Wplacement-новая =]

(Вы все еще должны получить concrete от abstract).

Смежные вопросы