Сбой отладки при оптимизации малых объектов для стирания типов
Я реализую класс, который выполняет стирание типов для небольших объектов и столкнулся с ошибкой сегментации, которую я не понимаю.
Следующая программа:
#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()
не отправляется динамически правильно, но я не понимаю, почему.
Может ли кто-нибудь сказать, чего мне не хватает?2 ответа:
Вы не производили
concrete<T>
отabstract
, поэтому при построении объекта с помощью placementnew
не создается vtable. Таким образом, при попытке вызвать виртуальную функцию она завершится неудачей;concrete<T>
иabstract
на самом деле являются совершенно несвязанными типами в этом примере.Я бы рекомендовал использовать ключевое слово
override
, Если вы используете C++11 или новее, чтобы позволить компилятору генерировать ошибку в подобных случаях.
std::aligned_storage<4 * sizeof(void*)> storage_;
Это создает хранилище одного байта.
Аргумент template задает не размер объявленного объекта, а размер объекта, который может быть выделен в массиве соответствующего размера этого типа. Следовательно, вам нужно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-new=](вам все еще нужно выведите
concrete
изabstract
).