C++智能指针:共享指针与共享数据

Eli*_*sky 9 c++ qt smart-pointers

在这篇富有洞察力的文章中,其中一位Qt程序员试图解释Qt实现的各种智能指针.在开始时,他区分共享数据和共享指针本身:

首先,让我们直截了当:共享指针和共享数据之间存在差异.共享指针时,指针的值及其生命周期受智能指针类的保护.换句话说,指针是不变的.但是,指针指向的对象完全在其控制之外.我们不知道该对象是否可以复制,如果它是可分配的.

现在,共享数据涉及智能指针类,了解有关共享数据的信息.事实上,重点是数据正在共享,我们并不关心如何.在这一点上,指针被用于共享数据的事实是无关紧要的.例如,你真的不关心如何隐式共享Qt工具类,对吗?对您来说重要的是它们是共享的(从而减少了内存消耗),并且它们的工作就好像它们不同.

坦率地说,我只是不解释这个解释.文章评论中有一个澄清的请求,但我没有发现作者的解释充分.

如果你这样做明白这一点,请解释.这是什么区别,以及其他共享指针类(即来自boost或新的C++标准)如何适应这种分类?

提前致谢

Joh*_*itb 7

在稍后的评论中,他稍微澄清了这个问题

这是我试图在第一部分中解决的重点.当您使用QSharedPointer时,您将共享指针的所有权.该类仅控制和处理指针 - 其他任何内容(如访问数据)都在其范围之外.使用QSharedDataPointer时,您正在共享数据.该类用于隐式共享:因此它可能会分裂.

试图解释:

重要的是,"指针"并不意味着在这种情况下存储地址的对象,但它意味着对象所在的存储位置(地址本身).所以严格来说,我想,你必须说你正在分享这个地址.boost::shared_ptr因此是共享"指针"的智能指针.boost::intrusive_ptr或者另一个侵入式智能指针似乎也共享指针,虽然知道指向的对象(它有一个引用计数成员或函数递增/递减它).

示例:如果有人与您共享一个黑盒子并且他不知道黑盒子里有什么,它类似于共享指针(代表盒子),而不是数据(盒子里面的内容).事实上,你甚至不知道盒子里面的东西是可共享的(如果盒子里什么都没包含怎么办?).智能指针由你和其他人代表(你不共享,当然),但该地址是框,是共享的.

共享数据意味着智能指针足够了解指向的数据,它可能会改变指向的地址(这需要复制数据等).因此,指针现在可能指向不同的地址.由于地址不同,地址不再共享.这也是std::string在某些实现中也是如此:

std::string a("foo"), b(a);
 // a and b may point to the same storage by now.
std::cout << (void*)a.c_str(), (void*)b.c_str();
 // but now, since you could modify data, they will
 // be different
std::cout << (void*)&a[0], (void*)&b[0];
Run Code Online (Sandbox Code Playgroud)

共享数据并不一定意味着您有一个指针呈现给您.您可以使用一个std::string由纯粹的手段a[0]cout << a;从不接触任何的c_str()功能.仍然可以在幕后继续分享.许多Qt类和其他小部件工具包的类也会发生同样的事情,这称为隐式共享(或写入时的复制).所以我认为可以这样总结:

  • 共享指针:当我们复制智能指针时,我们总是指向相同的地址,这意味着我们共享指针值.
  • 共享数据:我们可能在不同时间指向不同的地址.暗示我们知道如何将数据从一个地址复制到另一个地址.

所以试图分类

  • boost::shared_ptr,boost::intrusive_ptr:分享指针,而不是数据.
  • QString,QPen,QSharedDataPointer:分享它包含的数据.
  • std::unique_ptr,std::auto_ptr(和QScopedPointer):既不共享指针,也不共享数据.


Tim*_*sch 1

在第一种情况下,您向指针添加一个间接级别,以便智能指针表示的对象包装原始指针。只有一个指向该对象的指针,包装器的工作就是跟踪对原始指针的引用。一段非常简单的代码可能如下所示:

template<typename T>
struct smart_ptr {
    T    *ptr_to_object;
    int  *ptr_to_ref_count;
};
Run Code Online (Sandbox Code Playgroud)

当您复制结构时,您的复制/分配代码必须确保引用计数递增(或者如果对象被销毁则递减),但指向实际包装对象的指针永远不会改变,只能进行浅复制。由于结构体非常小,因此复制起来既简单又便宜,您所要做的“所有”就是操作引用计数。

在第二种情况下,它对我来说更像是一个对象存储库。“隐式共享”部分表明您可以FooWidget通过执行类似的操作来请求框架BarFoo.getFooWidget(),即使它看起来像指针(智能与否),您返回的是指向新对象的指针,但实际上您正在被传递指向保存在某种对象缓存中的现有对象的指针。从这个意义上说,它可能更类似于通过调用工厂方法获得的类似单例的对象。

至少这对我来说是这样的区别,但我可能离目标太远了,我需要谷歌地图才能找到回去的路。