Приведение между двумя шаблонно-шаблонными классами с использованием общих указателей

У меня есть класс из библиотеки, например так:

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);

Если вы действительно пытаетесь повторно использовать один и тот же объект для типов данных, имеющих одинаковый размер, без необходимости выделять новый объект с новым типом из сторонней библиотеки, у вас есть ограниченный выбор:

  1. Вы не должны выделять shared_ptr из необработанного указателя, в противном случае он удаляется дважды, вызывая ошибку сегмента;
  2. Вы либо повторно используете тип (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();

Вы можете улучшить это решение, используя собственный распределитель и пул.

Другие вопросы по тегам