shared_array的元素为shared_ptr?

时间:2013-03-21 13:50:26

标签: c++ boost c++11 shared-ptr

如果我有boost::shared_array<T>(或boost::shared_ptr<T[]>),有没有办法获得与数组共享的boost::shared_ptr<T>

例如,我可能想写:

shared_array<int> array(new int[10]);
shared_ptr<int> element = &array[2];

我知道我无法使用&array[2],因为它只有int *类型,shared_ptr<int>拥有一个隐式构造函数来获取该类型会很危险。理想情况下shared_array<int>会有一个实例方法,例如:

shared_ptr<int> element = array.shared_ptr_to(2);

不幸的是我找不到这样的东西。 shared_ptr<int>上有一个别名构造函数,它将与另一个shared_ptr<T>别名,但不允许使用shared_array<T>别名;所以我也写不出来(它不会编译):

shared_ptr<int> element(array, &array[2]);
//Can't convert 'array' from shared_array<int> to shared_ptr<int>

我使用的另一个选项是使用std::shared_ptr<T>std代替boost)。 T[]的专业化不是标准化的,所以我想自己定义一下。不幸的是,我认为实际上不可能以不破坏别名构造函数内部的方式,因为它试图将我的std::shared_ptr<T[]>强制转换为它自己的特定于实现的超类型,这是不可能的。 (我目前只是从增强版继承而来。)这个想法的好处在于我可以实现我的实例shared_ptr_to方法。

这是我尝试过的另一个想法,但我认为它不足以被接受为我们可能在整个大型项目中使用的东西。

template<typename T>
boost::shared_ptr<T> GetElementPtr(const boost::shared_array<T> &array, size_t index) {
    //This deleter works by holding on to the underlying array until the deleter itself is deleted.
    struct {
        boost::shared_array<T> array;
        void operator()(T *) {} //No action required here.
    } deleter = { array };
    return shared_ptr<T>(&array[index], deleter);
}

接下来我要尝试升级到Boost 1.53.0(我们目前只有1.50.0),使用shared_ptr<T[]>代替shared_array<T>,并且始终使用{{1而不是boost(即使对于非数组)。我希望这会有效,但我还没有机会尝试它:

std

当然我还是更喜欢实例方法语法,但我想我对那个没有好处(没有修改Boost):

shared_ptr<int[]> array(new int[10]);
shared_ptr<int> element(array, &array[2]);

其他人有什么想法吗?

3 个答案:

答案 0 :(得分:1)

你做的很奇怪。 为什么你需要shared_ptr元素?您是否希望将数组元素传递到其他位置并从删除中按住数组?

如果是,那么std::vector<shared_ptr<T>>更适合于此。该解决方案是安全的,标准的,并且在删除对象时具有精细的粒度

答案 1 :(得分:0)

boost::shared_ptr似乎并不支持这种做法。也许你可以使用自定义删除器来解决这个问题。但是std::shared_ptr提供了一个特殊的构造函数来支持你想要的东西:

struct foo
{
    int a;
    double b;
};

int main()
{
    auto sp1 = std::make_shared<foo>();
    std::shared_ptr<int> sp2 (sp1,&sp1->a);
}

此处,sp1sp2共享foo对象的所有权,但sp2指向其成员。如果sp1被销毁,则foo对象仍然有效且sp2仍然有效。

答案 2 :(得分:0)

这是我最后所做的。

我自己实现了shared_array<T>。它有效地扩展了shared_ptr<vector<T>>,除了它实际上扩展了我自己的vector<T>包装器,以便用户无法获取向量。这意味着我可以保证不会调整大小。然后我实现了我需要的实例方法 - 包括weak_ptr_to(size_t),当然还有operator[]

我的实现使用std::make_shared来生成向量。因此,向量与控制块分开分配其内部数组存储,但向量本身成为控制块的成员。因此,它等同于忘记将std::make_shared用于普通类型 - 但由于这些是数组,因此它们可能很大而且很少,所以它不那么重要。

我还可以创建一个基于shared_ptr<T>但带有default_delete<T[]>或者需要的实现的实现,但它必须从控制块中单独分配数组(因此与向量相比没有太多保存) )。我不认为有一种可移植的方法可以在控制块中嵌入动态大小的数组。

或者我的实现可以基于boost::shared_array<T>,并在获取元素指针时使用自定义删除器(根据问题中的示例)。在大多数情况下,这可能更糟糕,因为我们不是一次性命中分配数组,而是每次使用别名指针时都会遇到命中(对于非常短暂的指针,可能会发生很多事情)。

我认为让它更优化的唯一合理方法是使用最新的提升(如果它有效;在我改变主意之前我没有尝试过,主要是因为我对我的渴望自己的实例成员)。当然,这意味着在任何地方都使用boost个,即使对于单个对象也是如此。

但是,我使用的主要优点是Visual Studio的调试器(我被告知)擅长显示std :: shared_ptrs和std :: vectors的内容,并且(我们期待不太善于分析促进事物或习惯事物的内容。

所以我认为我所做的基本上是最优的。 :)