关于写入时复制和shared_ptr的困惑
Confusion about Copy-On-Write and shared_ptr
我已经在网上搜索并通读了有关shared_ptr
的 Boost 文档。 SO 上有一个响应,说写入时复制 (COW( 的shared_ptr
很糟糕,并且TR!
已将其从字符串库中删除。 关于SO的大多数建议都说使用shared_ptr
而不是常规指针。
该文档还讨论了使用 std::unique()
制作 COW 指针,但我还没有找到任何示例。
是在谈论拥有一个为您执行 COW 的智能指针,还是让您的对象使用克隆对象的新shared_ptr
然后修改克隆对象?
示例:食谱和成分
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;
}
困惑是如何用奶油代替oatmeal
食谱中的"牛奶",而不影响mashed_potatoes
食谱。
我的算法是:
locate pointer to `Milk` ingredient in the vector.
erase it.
append `Cream` ingredient to vector.
牛指针在这里如何发挥作用?
注意:我在Windows NT,Vista和7上使用MS Visual Studio 2010。
这里有几个问题捆绑在一起,所以如果我没有按照您期望的顺序解决它们,请耐心等待。
关于SO的大多数建议都说使用shared_ptr而不是常规指针。
是和不是。不幸的是,SO的许多用户建议shared_ptr
,就好像它是解决所有内存管理相关问题的灵丹妙药一样。不是。大多数建议都谈到不使用裸指针,这是实质性的不同。
真正的建议是使用智能管理器:无论是智能指针(unique_ptr
,scoped_ptr
,shared_ptr
,auto_ptr
(,智能容器(ptr_vector
,ptr_map
(还是针对难题的自定义解决方案(基于Boost.MultiIndex,使用侵入式计数器等(。
您应该根据需要选择要使用的智能管理器。最值得注意的是,如果您不需要共享对象的所有权,则不应使用 shared_ptr
。
什么是牛?
COW(写入时复制(是关于共享数据以"节省"内存并使复制更便宜...在不改变程序语义的情况下。
从用户的角度来看,std::string
是否使用COW并不重要。修改字符串时,所有其他字符串不受影响。
COW背后的想法是:
- 如果您是数据的唯一所有者,您可以对其进行修改
- 如果您不是,则应复制它,然后使用副本代替
它似乎与
shared_ptr
相似,那么为什么不呢?
它是相似的,但两者都是为了解决不同的问题,因此它们略有不同。
麻烦的是,由于无论所有权是否共享,shared_ptr
都是无缝运作的,因此COW很难实施"如果唯一所有者"测试。值得注意的是,weak_ptr
的相互作用使其变得困难。
显然,这是可能的。关键是 不要泄漏shared_ptr
,根本不要使用weak_ptr
(无论如何它们对 COW 毫无用处(。
有关系吗?
不,不是真的。事实证明,无论如何,COW并不是那么好。大多数时候,这是一个微优化...和一次微观悲观。您可以节省一些内存(尽管它仅在不复制大型对象时才有效(,但您使算法复杂化,这可能会减慢执行速度(您正在引入测试(。
我的建议是不要使用 COW。也不使用这些shared_ptr
。
就个人而言,我会:
- 使用
boost::ptr_vector<Ingredient>
而不是std::vector< boost::shared_ptr<Ingredient> >
(您不需要共享( - 创建一个
IngredientFactory
,这将创建(和管理(成分,并返回一个Ingredient const&
,Factory
应该比任何Receipt
都长寿。
编辑:在Xeo的评论之后,似乎最后一项(IngredientFactory
(非常简洁...
在IngredientFactory
的情况下,Receipt
对象将包含一个std::vector<Ingredient const*>
。请注意原始指针:
-
Receipt
不负责内存,但有权访问内存 - 有一个隐含的保证,即指向的对象将比
Receipt
对象的有效期更长
使用原始(裸(指针是可以的,只要您像对待引用一样对待它们。您只需要提防潜在的无效性,如果您愿意,您可以重新拔插它们 - 并且您相信提供商会处理生命周期/内存管理方面。
你没有什么可担心的。每个Recipe
对象都有自己的vector
,因此修改一个不会影响另一个,即使它们都恰好包含指向相同对象的指针。土豆泥配方只有在您更改p_milk
指向的对象的内容时才会受到影响,但您没有这样做。您正在修改oatmeal.m_ingredients
对象,该对象与mashed_potatoes.m_ingredients
完全无关。它们是两个完全独立的vector
实例。
- CLANG 编译器 说:变量"PTR"可能未初始化
- 在以唯一ptr为值的C++映射中,动态内存何时会被销毁
- 将 ptr 传递给 ptr 到 A 作为参数传递给 A 的函数是不好的做法吗?
- 为共享 ptr 向量实现复制 c'tor?
- 字符和整数中 **(ptr+1) 的值差异
- C++:在不中断共享的情况下通过引用传递共享 PTR?
- 如何将派生类从基 ptr 分配给 nlohmann::json
- 引用 std::shared:ptr 以避免引用计数
- 为什么我不能在不进行任何转换的情况下将浮点数放入任何类型的 ptr 中?
- 在调用函数时,ptr** 和 ptr*& 之间是否有区别,或者首选C++?
- 另一种类型的智能ptr,比如具有弱refs的unique_ptr
- 尝试打印出 *ptr++ 的值,以了解它是如何工作的
- 如何控制共享 ptr 引用计数?
- dopen():不以 root 身份运行时"failed to map segment from shared object"
- C++中的指针否定 (!ptr == NULL)
- 从const ptr*转换为ptr*时出现问题
- 无法使用 libtool 将 -shared 参数传递给 g++
- boost::shared_ptr和std::shared-ptr的同居
- 我可以用std::shared_ptr而不是boost::shared-ptr构建boost库吗
- shared-ptr-C++shared_ptr与unique_ptr用于资源管理