【问题标题】:Element of shared_array as shared_ptr?shared_array 的元素作为 shared_ptr?
【发布时间】:2013-03-21 13:50:25
【问题描述】:

如果我有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];

我知道我不能使用&amp;array[2],因为它只有int * 类型,而shared_ptr&lt;int&gt; 有一个采用该类型的隐式构造函数会很危险。理想情况下,shared_array&lt;int&gt; 会有一个实例方法,例如:

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

很遗憾,我找不到这样的东西。 shared_ptr&lt;int&gt; 上有一个别名构造函数,它将与另一个 shared_ptr&lt;T&gt; 别名,但它不允许使用 shared_array&lt;T&gt; 别名;所以我也不能写这个(它不会编译):

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

我使用的另一个选项是使用std::shared_ptr&lt;T&gt;std 而不是boost)。 T[] 的专业化没有标准化,所以我想自己定义。不幸的是,我认为这实际上不可能以不破坏别名构造函数内部的方式实现,因为它试图将我的std::shared_ptr&lt;T[]&gt; 转换为它自己的特定于实现的超类型,这不再可能。 (我的目前只是从 boost 继承。)这个想法的好处是我可以实现我的实例 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&lt;T[]&gt; 而不是 shared_array&lt;T&gt;,并且始终使用 boost 而不是 @987654344 @(即使对于非数组)。我希望这会起作用,但我还没有机会尝试:

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

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

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

其他人有什么想法吗?

【问题讨论】:

  • 您的删除器想法是应该完成的方式。它没有什么特别低效的(如果有疑问,请使用分析器进行实验)。
  • @n.m.我认为它会创建一个新的控制块(在堆上)供新的shared_ptr 参考。诚然,它并没有那么糟糕,但如果它可以共享同一个控制块,那就更好了。 :)
  • 您需要shared_array 吗?如果你有一个shared_ptr&lt;T&gt;(带有适当的删除器),你可以使用别名构造函数。
  • 从那以后我就想到了这一点。唯一缺少的是operator[]。我会发布一个答案,说明我最后做了什么。

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


【解决方案1】:

你在做奇怪的事情。 为什么你需要shared_ptr 元素?您是否希望将数组元素传递到其他地方并阻止您的数组被移除?

如果是,那么std::vector&lt;shared_ptr&lt;T&gt;&gt; 更适合。该解决方案安全、标准并且在对象移除方面具有精细的粒度

【讨论】:

  • 是的,只要我有一个指向任何元素的指针,我就希望保留整个数组。这种情况会发生在 API 使用 shared_ptr 以确保安全时,但有时我们会同时分配许多类似的对象,并希望最大限度地减少堆开销。同时,应用程序的其余部分并不特别需要知道对象来自数组。 - 虽然,我认为可能weak_ptr 更适合这些情况,但当然以上所有问题仍然适用,但在某些地方稍作调整说weak_ptr 而不是shared_ptr。
  • @entheh 那么数组的生命周期呢,什么时候发布呢?
  • 无论哪个对象拥有数组的 shared_ptr 都将负责。但是例如,有时我可能会将weak_ptrs 用于元素并将它们提供给Lua,并且总会有人不小心将对象在Lua 中保留太久。当这种情况发生时,我们想要定义的行为,仅此而已。 (我不确定 shared_ptr-to-element 案例是否会为我们提供。)
【解决方案2】:

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 仍然有效。

【讨论】:

  • 是的,这就是别名构造函数(boost 确实有它):)。不幸的是,它只需要一个shared_ptr&lt;anything&gt;,而我想传递一个shared_array&lt;something&gt;
  • 好像Boost 1.53及以上有shared_ptr&lt;T[]&gt;(见介绍结尾boost.org/doc/libs/1_54_0/libs/smart_ptr/shared_ptr.htm);不过,您可能需要使用它而不是 shared_array 才能使用该专业化。
【解决方案3】:

这就是我最后所做的。

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

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

我还可以创建一个基于shared_ptr&lt;T&gt; 但使用default_delete&lt;T[]&gt; 或任何需要的实现,但它必须与控制块分开分配数组(因此与向量相比没有太多节省)。我认为没有一种可移植的方式将动态大小的数组嵌入到控制块中。

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

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

但是,我使用的主要优点是 Visual Studio 的调试器(我听说)擅长显示 std::shared_ptrs 和 std::vectors 的内容,并且(我们期望)不太擅长分析提升事物或自定义事物的内容。

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

【讨论】:

    猜你喜欢
    • 2014-11-16
    • 1970-01-01
    • 1970-01-01
    • 2020-11-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-09-21
    相关资源
    最近更新 更多