很多QT中的类都是隐式共享的,它在作为参数传递时既安全又有效率。
上一讲中提到了一个implicit sharing的问题,说QT容器类是隐式共享的。文档中讲了一个这样的问题:
一些Qt函数返回一个容器,如果想使用STL风格的迭代器遍历某函数的返回值,就必须复制此容器并且遍历这个副本,例如,下面的代码给出了如何遍历由QSplitter::sizes()返回的QList<int>的正确方式。
// RIGHT
const QList<int> sizes = splitter->sizes();
QList<int>::const_iterator i;
for (i = sizes.begin(); i != sizes.end(); ++i)
...
// WRONG
QList<int>::const_iterator i;
for (i = splitter->sizes().begin();
i != splitter->sizes().end(); ++i)
...
错误的原因是,每次调用QSplitter::sizes()都返回一个新的QList<int>值,如果不存储这个值,C++在开始迭代之前就会自动将其销毁,而只留下一个浮动迭代器。更糟的是,每次循环运行由于都调用了splitter->sizes().end(),QSplitter::sizes()都必须生成一个新的列表的副本。总之,如果你使用STL风格迭代器,总是要在返回值的容器副本上进行遍历。而Java Style不需要,因为它自动生成了副本。
采用隐式共享,我们复制一个容器就像复制一个指针一样快。只有复制项之一发生改变时,数据才会实际被复制,而且这一切操作都可以在后台自动处理,由于这原因,隐式共享也叫“写时复制”。
隐式共享的优点是它是一个我们必须要考虑的最优化过程,它工作不需要我们干预,同时,隐式共享也提倡由值返回的对象的整洁编程风格。
很多类都是隐式共享的,除了容器类外还有QByteArray, QBrush, QFont, QPen, QImage, QPixmap, QString, QVariant 等等。
一个共享类对象创建,它的引用计数就记为1,如果将它传递给另一个对象,那计数就加1,并两个对象都指向内存中的相同的内部数据结构。如果某一个修改了对象值就会发生深度复制,以确保他们指向的数据结构不相同。并且之前的数据计数将减1,修改后的数据计数就为1。为1表示没有被共享。
这里简单讲下深度复制和浅度复制。
当处理一个共享对象时,我们有两种拷贝一个对象的方法:也就是我们常说的深度和浅度复制。深度复制是复制一个对象,浅度是复制一个对象引用,也就是说一个指针的共享数据块。显然使用浅度复制会很快。
隐式共享对象的复制操作符(operator=)是用浅度复制实现的。
当我们要实现自己的隐式共享类时,使用QSharedData, QSharedDataPointer类。
如果对象会改变或者引用计数是比另个更大,隐式共享就会自动从共享块中分离。一个隐式共享类完全控制它内部数据,如果任何成员函数修改了它的值,隐式共享都会在修改数据前自动分离。
例如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
QPainter paint;
paint.begin(&p2); // cuts p2 loose from p1
paint.drawText(0,50, "Hi");
paint.end();
例子中p1 p2共享数据,直到为p2调用begin()。
注意:你用non-const STL风格迭代器遍历容器(隐式共享容器,例如QMap,QVector等)时不需要复制容器。正如最开始讲的那个问题。
Output stream for debugging information | |
Access to directory structures and their contents | |
System-independent file information | |
Holds the environment variables that can be passed to a program | |
Convenient interface for working with URLs | |
Way to manipulate a key-value pairs in a URL's query | |
Used to locate data in a data model | |
Acts like a union for the most common Qt data types | |
Describes types of file or data, represented by a MIME type string | |
Array of bits | |
Array of bytes | |
Template class that provides a cache | |
Template class that provides a contiguous cache | |
Date and time functions | |
Template class that provides a hash-table-based dictionary | |
Convenience QHash subclass that provides multi-valued hashes | |
Template class that provides linked lists | |
Template class that provides lists | |
Converts between numbers and their string representations in various languages | |
Template class that provides a red-black-tree-based dictionary | |
Convenience QMap subclass that provides multi-valued maps | |
Generic container that provides a queue | |
Pattern matching using regular expressions | |
Pattern matching using regular expressions | |
The results of a matching a QRegularExpression against a string | |
Iterator on the results of a global match of a QRegularExpression object against a string | |
Template class that provides a hash-table-based set | |
Template class that provides a stack | |
Unicode character string | |
List of strings | |
Way of finding Unicode text boundaries in a string | |
Template class that provides a dynamic array |