【问题标题】:Casting between two type-templated classes using shared pointers使用共享指针在两个类型模板化的类之间进行转换
【发布时间】:2013-08-31 14:33:23
【问题描述】:

我有一个像这样的库提供的类:

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 的问题,但是这里不可能。

这只是一种简单的技术,它试图为具有相同大小的数据类型重用相同的对象,而不必分配具有新类型的新对象。

欢迎提出建议。

【问题讨论】:

  • 我相信有一个问题是你会在同一个内存上获得两个独立的共享指针。如果一个实例的引用计数降至零,则指针将被删除,而另一个仍然有效,但仍指向未定义的数据。
  • “假设我愿意接受 int 和 float 在这个特定架构上的大小始终相同”也是相同的对齐方式?即使您的编译器(对于您的架构)保证演员 reinterpret_cast&lt;TypedClass&lt;float&gt;* &gt;(int_object); 和对结果的操作将产生预期的行为,您仍然会产生 UB,正如 Erbureth 指出的那样(您的对象将被删除两次,每个 @ 一次) 987654325@ 和 float_obj 共享指针)。
  • 我过去已经解决了这个问题,继承自 boost enable_shared_from_this 类,但由于这是一个第三方库,所以在这里并不是一个真正的选择。我不知道另一种方法可以做到这一点。 (使用此信息更新了问题)。
  • 所以你需要一个 boost::shared_ptr&lt;TypedClass&lt;float&gt; &gt; 并且只有一个 boost::shared_ptr&lt;TypedClass&lt;int&gt; &gt; 并且你不能改变这些要求,因为它们是由第三方库强加的?

标签: c++ casting type-conversion smart-pointers reinterpret-cast


【解决方案1】:

shared_ptr&lt;T&gt; 有一个有趣的重载构造函数:

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

【讨论】:

  • 很高兴知道这个有趣的构造函数和重新解释指针转换。谢谢!
  • 注意:共享指针别名中的 TypedClass* 永远不会被删除。当别名超出范围并持有最后一个引用时, TypedClass* 将被删除。你可以看看 (codesynthesis.com/~boris/blog/2012/04/25/…) +1
【解决方案2】:

如果您真的尝试为具有相同大小的数据类型重用相同的对象,而不必从第三方库分配具有新类型的新对象,那么您的选择有限:

  1. 不应该从原始指针分配shared_ptr,否则会被删除两次,导致段错误;
  2. 您可以重新使用类型 (shared_ptr),以便可以通过 operator=() 直接“复制”;或 cat 原始指针,并确保您不进行影响内存分配/删除的更改。

作为你的例子,我建议如下代码:

float* float_ptr = reinterpret_cast<float*>(&int_object->m_data);
// Do something with *float_ptr
// And never delete it!

【讨论】:

    【解决方案3】:

    您可以使用boost pointer cast。这是一个非常丑陋的解决方案,但至少 ref 计数可以这样工作。

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

    【讨论】:

      【解决方案4】:

      我认为你不能,除非你用两个参数 typename 重载共享 ptr 类本身,因为它保留对数据的引用并在计数为 0 时删除。但是你必须从一个类型转到另一个, boost shared ptr, 会认为你已经发布了数据。

      shared_ptr p=ptr; // 如果 ptr 和 p 属于同一类型,则添加一个 ref。

      如果类型不同,则检索内部数据,然后释放它。

      另一种解决方案可能是使用 boost::any 将所有数据保存在此容器中。

      【讨论】:

        【解决方案5】:

        如果 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();
        

        您可以通过使用自定义分配器和池来改进此解决方案。

        【讨论】:

          猜你喜欢
          • 2021-01-27
          • 2013-05-07
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2013-10-31
          • 1970-01-01
          • 1970-01-01
          • 2022-07-10
          相关资源
          最近更新 更多