将shared_ptr保留在范围

时间:2016-08-12 11:20:03

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

我有一个类可以处理一堆共享指针,并将它们存储在各种(互斥)集合中。该类的任务之一是在添加/删除其中一个共享指针时使这些容器保持最新。

为了便于维护这些集合,我有一些帮助函数,它们对共享指针进行分类,在其所有相关容器中添加/删除它,并执行任何其他必要的工作。这很好除了,如果直接在容器的元素上调用remove函数,则在函数完成之前,共享指针是免费的。

我通过将元素传递给函数来解决这个问题。这为函数提供了自己的共享指针副本,并使其保持活动状态,直到函数结束。我很满意这个解决方案(需要/动机得到评论)但是这个函数不断被代码审计工具(例如,clang-tidy)标记/更改为性能不佳并更改为const引用。

我该如何避免这种情况?该函数是较大库的一小部分,维护者错过了注释是可以理解的。我无法更改代码审核规则,所以我想要一种简单有效的方法来避免这个问题?我怀疑C ++ 11和std::move可能有些聪明吗?

一个例子,如果我的班级使用FruitPtr共享指针,它可能有一些集合,例如,

std::vector<FruitPtr> greenFruit_;
std::vector<FruitPtr> redFruit_;
std::vector<FruitPtr> sweetFruit_;
std::vector<FruitPtr> sourFruit_;

有问题的功能看起来像

removeFruit(FruitPtr oldFruit)
{
    // Remove the element from any containers it belongs to:
    if (/*Some container condition*/)
    {
        //Find and remove from container
    }
    // etc., for all containers

    // Do some final operations on the element that must occur after it is removed from the containers,
    oldFruit->markSpoiled();
}

这样可行,但如果更改为removeFruit(const FruitPtr& oldFruit)则直接调用容器的元素,例如removeFruit(greenFruit_[i]),指针oldFruit在对元素本身执行最终操作之前,一旦从所有容器中移除它,它将被销毁。在我的库中,这些操作必须在函数结束时执行,因为它们会影响在容器中查找元素。

那么,我如何使这个函数与const引用一起使用,或者清楚地编写它不能编写的审计工具/读者?

修改 注意:

  • FruitPtrstd::shared_pointer<Fruit>
  • 指针的唯一副本可能位于removeFruit正在操作的容器中(假设它们是)。

3 个答案:

答案 0 :(得分:2)

我的直接解决方案是让函数自己制作副本,例如

success: function(data) {
            $('.container').html(data.html);
            $('.selectpicker').select2();
        }

但是有更聪明的东西吗?就像在C ++ 11中以某种方式removeFruit(const FruitPtr& oldFruit) { FruitPtr fruitToRemove(oldFruit) //... // Use fruitToRemove everywhere in the function, e.g., fruitToRemove->markSpoiled(); } 一样?

答案 1 :(得分:1)

聪明的指针是一个不必担心生命或指针的解决方案,但是,它们可以使简单的案例变得更加复杂。

让我假设FruitPtrstd::shared_ptr<Fruit>的typedef /用。

在这种情况下,我们可以假设此指针的唯一副本位于您的存储空间中,否则您不会崩溃。

简单的解决方案是编写以下内容:

void removeFruit(FruitPtr oldFruit);

通过执行此操作,您将在堆栈上获得shared_ptr的副本,之后将清理实际的实现。

假设您不想要shared_ptr的副本,可以写下以下内容:

void removeFruit(const FruitPtr &oldFruit) {
     FruitPtr tempStorage;
     switch (oldFruit.getType()){
         case Fruit::Type::Green: {
             auto itFind = std::find(begin(greenFruit_), end(greenFruit_), oldFruit);
             assert(itFind != end(greenFruit_));
             tempStorage = std::move(*itFind);
             greenFruit_.erase(itFind);
             break;
          }
          //...
     }
     // Some code
     tempStorage->markSpoiled();
} // Destroys instance if tempStorage.unique() == true.

最后,人们可能想知道为什么你甚至是markSpoiled函数。 这很可能是因为其他代码在实际上不应该拥有它时共享所有权。在这种情况下,std::weak_ptr就是您所需要的。

无论代码仍有一些对Fruit的引用,都会存储std::weak_ptr<Fruit>并且必须调用lock()来获取shared_ptr。如果删除此shared_ptr的最后一个实例,则此指针将为nullptr

答案 2 :(得分:0)

这个问题存在于两个层面:一个是编写实现所需所有权/生命周期语义的代码的技术层面,另一个是你和库维护者之间的沟通和管理问题 - 它似乎解决了第一个问题相当困难。

除了与维护人员交谈并耐心地和外交地解释他们对代码的更改改变其语义并需要经过思考并在您之间达成一致之外,我没有提出解决第二个问题的建议。虽然他们可能只是盲目地应用审计工具产生的建议,但他们可能有充分的理由坚持这些变化。当然,这对我来说很容易说,你很难做到。

技术问题的一种可能方法是让removeFruit()返回输入FruitPtr的副本 - 这将使Fruit对象保持活动状态,直到调用者决定如何处理它