Jam*_*lis 61
基本上,shared_ptr
有两个指针:一个指针,指向共享对象的指针和指向含有两个引用计数一个结构:一个用于"强引用,"或具有所有权的引用,一个用于"弱引用,"或引用不拥有所有权.
复制a时shared_ptr
,复制构造函数会增加强引用计数.当你破坏a时shared_ptr
,析构函数会减少强引用计数并测试引用计数是否为零; 如果是,则析构函数删除共享对象,因为没有shared_ptr
s指向它.
弱引用计数用于支持weak_ptr
; 基本上,无论weak_ptr
何时从a 创建shared_ptr
,弱引用计数都会递增,并且任何时候一个被销毁,弱引用计数就会递减.只要强引用计数或弱引用计数大于零,引用计数结构就不会被销毁.
实际上,只要强引用计数大于零,就不会删除共享对象.只要强引用计数或弱引用计数不为零,就不会删除引用计数结构.
val*_*ldo 16
我普遍同意James McNellis的回答.但是还有一点需要提及.
您可能知道,shared_ptr<T>
也可能在T
未完全定义类型时使用.
那是:
class AbraCadabra;
boost::shared_ptr<AbraCadabra> myPtr;
// ...
Run Code Online (Sandbox Code Playgroud)
这将编译和工作.与智能指针的许多其他实现不同,智能指针实际上要求完全定义封装类型以便使用它们.这与智能指针在不再被引用时知道删除封装对象的事实有关,并且为了删除对象,必须知道它是什么.
这是通过以下技巧实现的:shared_ptr
实际上包括以下内容:
上面的工厂是一个带有单个虚函数的辅助对象,它应该以正确的方式删除你的对象.
实际上,在为共享指针赋值时会创建此工厂.
也就是说,以下代码
AbraCadabra* pObj = /* get it from somewhere */;
myPtr.reset(pObj);
Run Code Online (Sandbox Code Playgroud)
这是该工厂的分配地点.注意:该reset
函数实际上是一个模板函数.它实际上为指定的类型(作为参数传递的对象的类型)创建工厂.这是您的类型应该完全定义的地方.也就是说,如果它仍未定义 - 您将收到编译错误.
另请注意:如果您实际创建派生类型的对象(派生自AbraCadabra
),并将其分配给shared_ptr
- 它将以正确的方式删除,即使您的析构函数不是虚拟的.在shared_ptr
根据是认为,在类型将始终删除对象reset
的功能.
所以shared_ptr是一个非常复杂的智能指针变体.它提供了极好的灵活性.但是,您应该知道,与智能指针的其他可能实现相比,这种灵活性的代价是性能极差.
另一方面 - 有所谓的"侵入式"智能指针.它们并不具备所有的灵活性,但相比之下它们可以提供最佳性能.
shared_ptr
与inrusive智能指针相比的优点:
shared_ptr
.这对于大型项目非常有价值,可以大大减少依赖性.的利弊shared_ptr
相比inrusive智能指针:
reset
.当一个shared_ptr
被分配给另一个 - 没有更多的分配.使用侵入式智能指针时,您可以自由地将智能指针与原始指针混合使用.这没关系,因为实际引用计数驻留在对象本身内部,这是单个的.相反 - shared_ptr
你可能不会与原始指针混在一起.
AbraCadabra*pObj =/*从某个地方获取它*/; myPtr.reset(pObj); // ... pObj = myPtr.get(); boost :: shared_ptr myPtr2(pObj); //哎呀
以上将崩溃.
MSa*_*ers 11
至少有三种众所周知的机制.
当创建第一个指向对象的共享指针时,会创建一个单独的引用计数对象并初始化为1.复制指针时,引用计数会增加; 当指针被销毁时,它会减少.指针赋值增加一个计数并减少另一个计数(按此顺序,否则自我赋值ptr=ptr
将中断).如果引用计数达到零,则不再存在指针,并删除该对象.
内部计数器要求指向的对象具有计数器字段.这通常通过从特定基类派生来实现.作为交换,这节省了引用计数的堆分配,并且它允许从原始指针重复创建共享指针(使用外部计数器,最终会为一个对象提供两个计数)
您可以将所有共享指针保存在循环图中的对象中,而不是使用计数器.创建的第一个指针指向自身.复制指针时,将副本插入圆圈.删除后,将其从圆圈中删除.但是当被破坏的指针指向自身时,即当它是唯一的指针时,您将删除指向的对象.
缺点是从循环单链表中删除节点相当昂贵,因为您必须迭代所有节点才能找到前一个节点.由于参考的局部性差,这可能特别痛苦.
可以组合第二个和第三个想法:基类可以是该循环图的一部分,而不是包含计数.当然,这意味着只有当对象指向自身时才能删除它(循环长度为1,没有剩余的指针).同样,优点是你可以从弱指针创建智能指针,但是从链中删除指针的糟糕表现仍然是个问题.
想法3的确切图形结构无关紧要.您还可以创建二叉树结构,在根位置指向对象.同样,硬操作是从该图中删除共享指针节点.好处是如果你在许多线程上有很多指针,那么增长的图形部分就不是一个高度竞争的操作.