简体   繁体   English

关于 Copy-On-Write 和 shared_ptr 的困惑

[英]Confusion about Copy-On-Write and shared_ptr

I have searched the web and read through the Boost documentation about shared_ptr .我搜索了 web 并阅读了有关shared_ptr的 Boost 文档。 There is a response on SO that says that shared_ptr for Copy-On-Write (COW) sucks and that TR! SO上有一个回复说用于写时复制(COW)的shared_ptr很糟糕而且TR! has removed it from the string libraries.已将其从字符串库中删除。 Most advice on SO says to use shared_ptr rather than regular pointers.大多数关于 SO 的建议都说使用shared_ptr而不是常规指针。

The documentation also talks about using std::unique() to make a COW pointer, but I haven't found any examples.该文档还讨论了使用std::unique()制作 COW 指针,但我没有找到任何示例。

Is the talk about having a smart pointer that performs COW for you or about having your object use a new shared_ptr to a cloned object then modifying the cloned object?是关于有一个智能指针为你执行 COW 还是关于让你的 object 使用新的shared_ptr到克隆的 object 然后修改克隆的 object?

Example: Recipes & Ingredients示例:食谱和配料

struct Nutrients;

struct Ingredient
{
    Ingredient(const std::string& new_title = std::string(""))
        : m_title(new_title)
        { ; }
    std::string m_title;
    Nutrients   ing_nutrients;
};

struct Milk : public Ingredient
    : Ingredient("milk")
{ ; }

struct Cream : public Ingredient
    : Ingredient("cream")
{ ; }

struct Recipe
{
    std::vector< boost::shared_ptr<Ingredient> > m_ingredients;
    void append_ingredient(boost::shared_ptr<Ingredient> new_ingredient)
    {
        m_ingredients.push_back(new_ingredient);
        return;
    }
    void replace_ingredient(const std::string& original_ingredient_title,
                            boost::shared_ptr<Ingredient> new_ingredient)
    {
        // Confusion here
    }
};

int main(void)
{
    // Create an oatmeal recipe that contains milk.
    Recipe  oatmeal;
    boost::shared_ptr<Ingredient> p_milk(new Milk);
    oatmeal.add_ingredient(p_milk);

    // Create a mashed potatoes recipe that contains milk
    Recipe  mashed_potatoes;
    mashed_potatoes.add_ingredient(p_milk);

    // Now replace the Milk in the oatmeal with cream
    // This must not affect the mashed_potatoes recipe.
    boost::shared_ptr<Ingredient> p_cream(new Cream);
    oatmeal.replace(p_milk->m_title, p_cream);

    return 0;
}

The confusion is how to replace the 'Milk' in the oatmeal recipe with Cream and not affect the mashed_potatoes recipe.困惑是如何用奶油代替oatmeal配方中的“牛奶”而不影响mashed_potatoes配方。

My algorithm is:我的算法是:

locate pointer to `Milk` ingredient in the vector.
erase it.
append `Cream` ingredient to vector.

How would a COW pointer come into play here?牛指针如何在这里发挥作用?

Note: I am using MS Visual Studio 2010 on Windows NT, Vista and 7.注意:我在 Windows NT、Vista 和 7 上使用 MS Visual Studio 2010。

There are several questions bundled into one here, so bear with me if I don't address them in the order you would expect.这里有几个问题捆绑在一起,所以如果我没有按照您期望的顺序解决这些问题,请耐心等待。

Most advice on SO says to use shared_ptr rather than regular pointers.大多数关于 SO 的建议都说使用 shared_ptr 而不是常规指针。

Yes and No. A number of users of SO, unfortunately, recommend shared_ptr as if it were a silver bullet to solve all memory management related issues.是和否。不幸的是,许多 SO 用户推荐shared_ptr好像它是解决所有 memory 管理相关问题的灵丹妙药。 It is not .它不是 Most advice talk about not using naked pointers, which is substantially different.大多数建议都在谈论不使用裸指针,这是完全不同的。

The real advice is to use smart managers : whether smart pointers ( unique_ptr , scoped_ptr , shared_ptr , auto_ptr ), smart containers ( ptr_vector , ptr_map ) or custom solutions for hard problems (based on Boost.MultiIndex, using intrusive counters, etc...).真正的建议是使用智能管理器:无论是智能指针( unique_ptrscoped_ptrshared_ptrauto_ptr )、智能容器( ptr_vectorptr_map )还是针对难题的自定义解决方案(基于Boost.MultiIndex,使用侵入式计数器等... )。

You should pick the smart manager to use depending on the need.您应该根据需要选择要使用的智能管理器。 Most notable, if you do not need to share the ownership of an object, then you should not use a shared_ptr .最值得注意的是,如果您不需要共享 object 的所有权,则不应使用shared_ptr

What is COW?什么是牛?

COW (Copy-On-Write) is about sharing data to "save" memory and make copy cheaper... without altering the semantic of the program. COW(Copy-On-Write)是关于共享数据以“保存” memory 并使复制更便宜......而不改变程序的语义。

From a user point of view, whether std::string use COW or not does not matter.从用户的角度来看, std::string是否使用 COW 并不重要。 When a string is modified, all other strings are unaffected.当一个字符串被修改时,所有其他字符串都不受影响。

The idea behind COW is that: COW 背后的理念是:

  • if you are the sole owner of the data, you may modify it如果您是数据的唯一所有者,您可以对其进行修改
  • if you are not, then you shall copy it, and then use the copy instead如果不是,则应复制它,然后改用该副本

It seems similar to shared_ptr , so why not?它似乎类似于shared_ptr ,为什么不呢?

It is similar, but both are meant to solve different problems, and as a result they are subtly different.它是相似的,但两者都是为了解决不同的问题,因此它们有细微的不同。

The trouble is that since shared_ptr is meant to function seamlessly whether or not the ownership is shared, it is difficult for COW to implement the "if sole owner" test.问题在于,由于shared_ptr意味着无论所有权是否共享都无缝地连接到 function,因此 COW 很难实现“如果唯一所有者”测试。 Notably, the interaction of weak_ptr makes it difficult.值得注意的是, weak_ptr的交互使其变得困难。

It is possible, obviously.显然,这是可能的。 The key is not to leak the shared_ptr , at all, and not to use weak_ptr (they are useless for COW anyway).关键是根本不要泄漏shared_ptr ,也不要使用weak_ptr (无论如何它们对 COW 没用)。

Does it matter?有关系吗?

No, not really.不,不是真的。 It's been proved that COW is not that great anyway.事实证明,无论如何,COW 并不是那么好。 Most of the times it's a micro optimization... and a micro pessimization at once.大多数时候,它是一种微优化……同时也是一种微悲观。 You may spare some memory (though it only works if you don't copy large objects), but you are complicating the algorithm, which may slow down the execution (you are introducing tests).您可以节省一些 memory (尽管它仅在您不复制大对象时才有效),但是您使算法复杂化,这可能会减慢执行速度(您正在引入测试)。

My advice would be not to use COW.我的建议是不要使用 COW。 And not to use those shared_ptr either.也不要使用那些shared_ptr


Personnally, I would either:就个人而言,我会:

  • use boost::ptr_vector<Ingredient> rather than std::vector< boost::shared_ptr<Ingredient> > (you do not need sharing)使用boost::ptr_vector<Ingredient>而不是std::vector< boost::shared_ptr<Ingredient> > (您不需要共享)
  • create a IngredientFactory , that would create (and manage) the ingredients, and return a Ingredient const& , the Factory should outlive any Receipt .创建一个IngredientFactory ,它将创建(和管理)成分,并返回一个Ingredient const& ,该Factory应该比任何Receipt寿命更长。

EDIT : following Xeo's comment, it seems the last item ( IngredientFactory ) is quite laconic...编辑:在 Xeo 的评论之后,似乎最后一项( IngredientFactory )非常简洁......

In the case of the IngredientFactory , the Receipt object will contain a std::vector<Ingredient const*> .对于IngredientFactoryReceipt object 将包含std::vector<Ingredient const*> Note the raw pointer:注意原始指针:

  • Receipt is not responsible for the memory, but is given access to it Receipt不对 memory 负责,但可以访问它
  • there is an implicit warranty that the object pointed to will remain valid longer than the Receipt object object 指向的隐含保证将比Receipt object 的有效期更长

It is fine to use raw (naked) pointers, as long as you treat them like you would a reference.使用原始(裸)指针很好,只要您像对待引用一样对待它们。 You just have to beware of potential nullity, and you're offered the ability to reseat them if you so wish -- and you trust the provider to take care of the lifetime / memory management aspects.您只需要提防潜在的无效性,如果您愿意,您可以重新安装它们——并且您相信提供商会照顾好生命周期/memory 管理方面。

You have nothing to worry about.你没有什么可担心的。 Each Recipe object has its own vector , so modifying one won't affect the other, even though both of them happen to contain pointers to the same objects.每个Recipe object 都有自己的vector ,因此修改一个不会影响另一个,即使它们都包含指向相同对象的指针。 The mashed-potatoes recipe would only be affected if you changed the contents of the object that p_milk points at, but you're not doing that.只有当您更改p_milk指向的 object 的内容时,土豆泥配方才会受到影响,但您没有这样做。 You're modifying the oatmeal.m_ingredients object, which has absolutely no relation to mashed_potatoes.m_ingredients .您正在修改oatmeal.m_ingredients object,它与mashed_potatoes.m_ingredients完全没有关系。 They're two completely independent vector instances.它们是两个完全独立的vector实例。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM