Приведение между двумя шаблонно-шаблонными классами с использованием общих указателей
У меня есть класс из библиотеки, например так:
template <typename T>
class TypedClass
{
public:
typedef typename boost::shared_ptr<TypedClass<T> > Ptr;
T m_data;
T* m_pointer_data;
};
Предполагая, что я согласен с тем, что int и float всегда имеют одинаковый размер (и выравнивание) в этой конкретной архитектуре, мне кажется, это действительно так:
TypedClass<int>* int_object = new TypedClass<int>();
TypedClass<float>* float_object = reinterpret_cast<TypedClass<float>* >(int_object);
Теперь я пытаюсь добиться того же, используя boost shared_ptrs, и придумал это:
TypedClass<int>::Ptr int_object = TypedClass<int>::Ptr(new TypedClass<int>());
void* int_object_void_pointer = reinterpret_cast<void*>(int_object.get());
TypedClass<float>::Ptr float_object(reinterpret_cast<TypedClass<float>*>(int_object_void_pointer));
Что, кажется, работает нормально, но использование общих указателей приведет к тому, что объект будет удален дважды, чего я бы хотел избежать.
Важно отметить, что TypedClass является частью сторонней библиотеки и что эта библиотека использует общие указатели для всех своих внутренних функций, поэтому мне нужны данные в этой форме. Ранее я решил эту проблему, наследуя от boost enable_shared_from_this, но здесь это невозможно.
Это простой метод, позволяющий повторно использовать один и тот же объект для типов данных, имеющих одинаковый размер, без необходимости выделять новый объект с новым типом.
Предложения приветствуются.
5 ответов
shared_ptr<T>
имеет интересный перегруженный конструктор:
template<class Y> shared_ptr(shared_ptr<Y> const & r, element_type * p);
По сути, это создает shared_ptr, который принимает удалитель и пересчет из r
за исключением того, что он держит p
,
Вы можете использовать это так:
TypedClass<int>::Ptr int_object = TypedClass<int>::Ptr(new TypedClass<int>());
TypedClass<float>::Ptr float_object(int_object,reinterpret_cast<TypedClass<float>*>(int_object.get()));
РЕДАКТИРОВАТЬ:
Если вы используете Boost >= 1.53.0, есть также boost::reinterpret_pointer_cast
, Таким образом, вы можете написать:
TypedClass<float>::Ptr float_object = boost::reinterpret_pointer_cast<TypedClass<float> >(int_object);
Если вы действительно пытаетесь повторно использовать один и тот же объект для типов данных, имеющих одинаковый размер, без необходимости выделять новый объект с новым типом из сторонней библиотеки, у вас есть ограниченный выбор:
- Вы не должны выделять shared_ptr из необработанного указателя, в противном случае он удаляется дважды, вызывая ошибку сегмента;
- Вы либо повторно используете тип (shared_ptr), так что вы можете напрямую "копировать" с помощью operator=(); или присвойте необработанный указатель и убедитесь, что вы НЕ вносите изменения, которые влияют на распределение / удаление памяти.
В качестве вашего примера я предлагаю код, подобный приведенному ниже:
float* float_ptr = reinterpret_cast<float*>(&int_object->m_data);
// Do something with *float_ptr
// And never delete it!
Вы можете использовать повышение указателя приведения. Это очень уродливое решение, но по крайней мере подсчет ссылок будет работать таким образом.
TypedClass<int>::Ptr int_object = TypedClass<int>::Ptr(new TypedClass<int>());
TypedClass<float>::Ptr float_object = boost::static_pointer_cast<TypedClass<float>>(boost::shared_ptr<void>(int_object));
Я думаю, что вы не можете, кроме случаев, когда вы перегружаете сам общий класс ptr двумя параметрами typename, так как он сохраняет ссылку на данные и удаляет, когда счетчик равен 0. Но так как вы должны перейти от типа к другому, повышение поделился ptr, будет думать, что вы все равно выпустили данные.
shared_ptr p = ptr; // добавляет ссылку, если ptr и p одного типа.
если тип не совпадает, вы извлекаете внутренние данные и затем освобождаете их.
другим решением может быть хранение всех данных в этом контейнере с помощью boost::any.
Если TypedClass размещен в вашем коде (а не во внешней библиотеке), вы можете использовать определенный деструктор для предотвращения множественного уничтожения:
template<class T>
struct NullDestructor
{
void operator()(TypedClass<T> *& t) { /* nothing to do */ }
};
template<class T>
typename TypedClass<T>::Ptr make_fake_shared_ptr( TypedClass<T> * ptr )
{
return typename TypedClass<T>::Ptr(ptr, NullDestructor<T>() );
}
TypedClass<int>::Ptr int_object = make_fake_shared_ptr<int>(new TypedClass<int>());
TypedClass<float> * ptr = reinterpret_cast<TypedClass<float>*>(int_object.get());
TypedClass<float>::Ptr float_object = make_fake_shared_ptr<float>(ptr);
С этим решением вы отвечаете за уничтожение памяти вручную в конце:
delete float_object.get();
Вы можете улучшить это решение, используя собственный распределитель и пул.