之前也大致了解过Qt中的隐式共享,一直以为这是Qt中搞得跟C++中shared_ptr
类似的,就是引用计数那一套东西,还有opencv中的Mat
也是类似的机制,今天无意间把Qt的文档中的隐式共享(Implicit Sharing)仔细看了一遍,发现自己只理解了一半。
共享数据
隐式共享的类通常包含一个指向共享数据块的指针,该数据块中包含了引用计数和实际的数据。当创建一个共享对象时,引用计数初始化为1;每当有新的对象引用该共享数据时,引用计数就会增加;当不再引用该共享数据时,引用计数会减少;当引用计数变为0时,共享数据会被删除。
以上的文档中翻译。这一部分其实就是类似shared_ptr
的功能,比如:
QPixmap p1, p2;
p1.load("image.bmp");
p2 = p1; // p1 and p2 share data
虽然p1, p2
都是类对象,并没有显式的共享指针,但是Qt内部依旧会让p1,p2
共享同一块内存数据,即浅拷贝(Shallow Copy),不会有大量的资源开销。所以你可以在赋值、传参、返回值中尽情使用。
实现
当你需要实现自己的隐式共享类时,Qt提供了QSharedData
和QSharedDataPointer
这两个辅助类来简化开发工作。QSharedData
类定义了一个共享数据结构,包括引用计数和其他必要的机制,而QSharedDataPointer
则提供了一个智能指针来管理QSharedData的实例。
另外很重要的一点,这种隐式共享(ImplicitSharing)是线程安全的。
自动解耦
当一个对象即将改变其内部数据并且当前的引用计数大于1时,隐式共享会自动使该对象与其共享的数据块分离。
这意味着,如果多个对象共享同一份数据,那么当其中一个对象尝试修改数据时,它会创建一个自己的数据副本,并在其副本上进行修改,而不影响其他仍然共享原来数据的对象。
隐式共享的类对其内部数据具有控制权。在任何修改其内部数据的成员函数中,该类会在实际修改之前自动解耦。例如,QPen
类在所有更改其内部数据的成员函数中都会先解耦再修改。
void QPen::setStyle(Qt::PenStyle style)
{
detach(); // 解耦共享数据
d->style = style; // 设置样式成员
}
void QPen::detach()
{
if (d->ref != 1) { // 如果引用计数不等于1
... // 执行深拷贝
}
}
当修改QPen
的内部数据style
时,会先调用detach()
,而detach()
的作用就是:深度拷贝一份共享数据。然后在独立的数据上进行修改,而不会影响原来的数据。
注意事项
虽然隐式共享在大多数情况下都是透明的,但在某些场景下,如迭代容器元素时,Qt的迭代器行为与STL的标准迭代器有所不同。这是因为Qt的迭代器可能会持有对容器内部数据的弱引用,从而影响迭代器的有效性。因此,在使用Qt的容器和迭代器时,需要注意这一点。下面是官方举例:
QVector<int> a, b;
a.resize(100000); // 创建一个包含 100000 个元素的大向量,初始值为 0。
QVector<int>::iterator i = a.begin();
// 错误的迭代器使用方式:
b = a;
/*
此时,迭代器 i 指向共享的数据。
如果我们执行 *i = 4,将会改变共享的数据(两个向量都会受到影响)。
这种行为不同于 STL 容器。在 Qt 中应避免这样做。
*/
a[0] = 5;
/*
此时容器 a 已经从共享数据中分离出来,
即使 i 是从容器 a 开始的迭代器,现在它实际上指向的是 b 中的数据。
此时 (*i) 的值仍然是 0。
*/
b.clear(); // 迭代器 i 现在完全无效了。
int j = *i; // 未定义的行为!
/*
b 中的数据(i 指向的数据)已经不存在。
如果是 STL 容器,(*i) 的值将是 5,并且是定义良好的,
但在 QVector 中,这很可能会导致崩溃。
*/
详细见https://doc.qt.io/qt-5/containers.html#implicit-sharing-iterator-problem
支持隐式共享的类
几乎涵盖了Qt所有的数据类型:
类名 | 描述 |
---|---|
QBitArray | Array of bits |
QBitmap | Monochrome (1-bit depth) pixmaps |
QBrush | Defines the fill pattern of shapes drawn by QPainter |
QByteArray | Array of bytes |
QByteArrayList | List of byte arrays |
QCache | Template class that provides a cache |
QCollator | Compares strings according to a localized collation algorithm |
QCollatorSortKey | Can be used to speed up string collation |
QCommandLineOption | Defines a possible command-line option |
QContiguousCache | Template class that provides a contiguous cache |
QCursor | Mouse cursor with an arbitrary shape |
QDBusPendingCall | Refers to one pending asynchronous call |
QDBusUnixFileDescriptor | Holds one Unix file descriptor |
QDateTime | Date and time functions |
QDebug | Output stream for debugging information |
QDir | Access to directory structures and their contents |
QDnsDomainNameRecord | Stores information about a domain name record |
QDnsHostAddressRecord | Stores information about a host address record |
QDnsMailExchangeRecord | Stores information about a DNS MX record |
QDnsServiceRecord | Stores information about a DNS SRV record |
QDnsTextRecord | Stores information about a DNS TXT record |
QFileInfo | System-independent file information |
QFont | Specifies a query for a font used for drawing text |
QFontInfo | General information about fonts |
QFontMetrics | Font metrics information |
QFontMetricsF | Font metrics information |
QGlyphRun | Direct access to the internal glyphs in a font |
QGradient | Used in combination with QBrush to specify gradient fills |
QHash | Template class that provides a hash-table-based dictionary |
QHostAddress | IP address |
QHttp2Configuration | Controls HTTP/2 parameters and settings |
QHttpPart | Holds a body part to be used inside a HTTP multipart MIME message |
QIcon | Scalable icons in different modes and states |
QImage | Hardware-independent image representation that allows direct access to the pixel data, and can be used as a paint device |
QJsonArray | Encapsulates a JSON array |
QJsonDocument | Way to read and write JSON documents |
QJsonObject | Encapsulates a JSON object |
QJsonParseError | Used to report errors during JSON parsing |
QJsonValue | Encapsulates a value in JSON |
QKeySequence | Encapsulates a key sequence as used by shortcuts |
QList | Template class that provides lists |
QLocale | Converts between numbers and their string representations in various languages |
QMap | Template class that provides a red-black-tree-based dictionary |
QMimeType | Describes types of file or data, represented by a MIME type string |
QMultiHash | Convenience QHash subclass that provides multi-valued hashes |
QMultiMap | Convenience QMap subclass that provides multi-valued maps |
QNetworkAddressEntry | Stores one IP address supported by a network interface, along with its associated netmask and broadcast address |
QNetworkCacheMetaData | Cache information |
QNetworkCookie | Holds one network cookie |
QNetworkInterface | Listing of the host’s IP addresses and network interfaces |
QNetworkProxy | Network layer proxy |
QNetworkProxyQuery | Used to query the proxy settings for a socket |
QNetworkRequest | Holds a request to be sent with QNetworkAccessManager |
QOpenGLDebugMessage | Wraps an OpenGL debug message |
QPainterPath | Container for painting operations, enabling graphical shapes to be constructed and reused |
QPalette | Contains color groups for each widget state |
QPen | Defines how a QPainter should draw lines and outlines of shapes |
QPersistentModelIndex | Used to locate data in a data model |
QPicture | Paint device that records and replays QPainter commands |
QPixmap | Off-screen image representation that can be used as a paint device |
QPolygon | Vector of points using integer precision |
QPolygonF | Vector of points using floating point precision |
QProcessEnvironment | Holds the environment variables that can be passed to a program |
QQueue | Generic container that provides a queue |
QRawFont | Access to a single physical instance of a font |
QRegExp | Pattern matching using regular expressions |
QRegion | Specifies a clip region for a painter |
QRegularExpression | Pattern matching using regular expressions |
QRegularExpressionMatch | The results of a matching a QRegularExpression against a string |
QRegularExpressionMatchIterator | Iterator on the results of a global match of a QRegularExpression object against a string |
QSet | Template class that provides a hash-table-based set |
QSslCertificate | Convenient API for an X509 certificate |
QSslCertificateExtension | API for accessing the extensions of an X509 certificate |
QSslCipher | Represents an SSL cryptographic cipher |
QSslConfiguration | Holds the configuration and state of an SSL connection |
QSslDiffieHellmanParameters | Interface for Diffie-Hellman parameters for servers |
QSslError | SSL error |
QSslKey | Interface for private and public keys |
QSslPreSharedKeyAuthenticator | Authentication data for pre shared keys (PSK) ciphersuites |
QStack | Template class that provides a stack |
QStaticText | Enables optimized drawing of text when the text and its layout is updated rarely |
QStorageInfo | Provides information about currently mounted storage and drives |
QString | Unicode character string |
QStringList | List of strings |
QTextBlockFormat | Formatting information for blocks of text in a QTextDocument |
QTextBoundaryFinder | Way of finding Unicode text boundaries in a string |
QTextCharFormat | Formatting information for characters in a QTextDocument |
QTextCursor | Offers an API to access and modify QTextDocuments |
QTextDocumentFragment | Represents a piece of formatted text from a QTextDocument |
QTextFormat | Formatting information for a QTextDocument |
QTextFrameFormat | Formatting information for frames in a QTextDocument |
QTextImageFormat | Formatting information for images in a QTextDocument |
QTextListFormat | Formatting information for lists in a QTextDocument |
QTextTableCellFormat | Formatting information for table cells in a QTextDocument |
QTextTableFormat | Formatting information for tables in a QTextDocument |
QUrl | Convenient interface for working with URLs |
QUrlQuery | Way to manipulate a key-value pairs in a URL’s query |
QVariant | Acts like a union for the most common Qt data types |
QVector | Template class that provides a dynamic array |