Qt 隐式共享
Qt中的许多c++类使用隐式数据共享来最大化资源使用并最小化复制。隐式共享类作为参数传递时既安全又高效,因为只传递指向数据的指针,并且只有当函数写入数据时才会复制数据,即写时复制。
概述
共享类由指向包含引用计数和数据的共享数据块的指针组成。
当创建共享对象时,它将引用计数设置为1。每当新对象引用共享数据时,引用计数递增,当对象取消引用共享数据时,引用计数递减。当引用计数变为零时,将删除共享数据。
在处理共享对象时,有两种复制对象的方法。我们通常讲深拷贝和浅拷贝。深度拷贝意味着复制一个对象。浅拷贝是一种引用拷贝,即只是一个指向共享数据块的指针。就内存和CPU而言,进行深度复制可能代价高昂。创建浅拷贝非常快,因为它只需要设置一个指针并增加引用计数。
隐式共享对象的对象赋值(带operator=())是使用浅拷贝实现的。
共享的好处是程序不需要不必要地复制数据,从而减少内存使用和数据复制。对象可以很容易地分配,作为函数参数发送,并从函数返回。
隐性分享大多发生在幕后;程序员很少需要担心这个问题。然而,Qt的容器迭代器与STL中的容器迭代器具有不同的行为。阅读隐式共享迭代器问题。
在多线程应用程序中,隐式共享发生,如线程和隐式共享类中所述。
在实现自己的隐式共享类时,请使用QSharedData和QSharedDataPointer类。
细节
如果对象即将更改且引用计数大于1,隐式共享会自动将对象从共享块中分离出来。(这通常被称为写时复制或值语义。)
隐式共享类控制其内部数据。在任何修改其数据的成员函数中,它在修改数据之前自动分离。但是,请注意容器迭代器的特殊情况;参见隐式共享迭代器问题。
QPen类使用隐式共享,从所有更改内部数据的成员函数中分离共享数据。
代码段:
void QPen::setStyle(Qt::PenStyle style) { detach(); // detach from common data 与公共数据分离 d->style = style; // set the style member 设置样式成员 } void QPen::detach() { if (d->ref != 1) { ... // perform a deep copy 执行深度复制 } }
类列表
如果要更改对象,下面列出的类将自动与公共数据分离。程序员甚至不会注意到对象是共享的。因此,您应该将它们的单独实例视为单独的对象。它们将始终作为单独的对象,但在任何可能的情况下都具有共享数据的额外好处。出于这个原因,您可以将这些类的实例作为参数按值传递给函数,而不必担心复制开销。
QPixmap p1, p2; p1.load("image.bmp"); p2 = p1; // p1 and p2 share data P1和p2共享数据【浅拷贝】 QPainter paint; paint.begin(&p2); // cuts p2 loose from p1 将p2从p1中分离出来【深拷贝】 paint.drawText(0,50, "Hi"); paint.end();
在这个例子中,p1和p2共享数据,直到对p2调用QPainter::begin(),因为绘制像素图会修改它。
【警告】:在使用stl风格的迭代器时,要小心复制隐式共享容器(QMap、QVector等)。参见隐式共享迭代器问题。