Qt中的隐式共享(Implicit Sharing)

之前也大致了解过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提供了QSharedDataQSharedDataPointer这两个辅助类来简化开发工作。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所有的数据类型:

类名描述
QBitArrayArray of bits
QBitmapMonochrome (1-bit depth) pixmaps
QBrushDefines the fill pattern of shapes drawn by QPainter
QByteArrayArray of bytes
QByteArrayListList of byte arrays
QCacheTemplate class that provides a cache
QCollatorCompares strings according to a localized collation algorithm
QCollatorSortKeyCan be used to speed up string collation
QCommandLineOptionDefines a possible command-line option
QContiguousCacheTemplate class that provides a contiguous cache
QCursorMouse cursor with an arbitrary shape
QDBusPendingCallRefers to one pending asynchronous call
QDBusUnixFileDescriptorHolds one Unix file descriptor
QDateTimeDate and time functions
QDebugOutput stream for debugging information
QDirAccess to directory structures and their contents
QDnsDomainNameRecordStores information about a domain name record
QDnsHostAddressRecordStores information about a host address record
QDnsMailExchangeRecordStores information about a DNS MX record
QDnsServiceRecordStores information about a DNS SRV record
QDnsTextRecordStores information about a DNS TXT record
QFileInfoSystem-independent file information
QFontSpecifies a query for a font used for drawing text
QFontInfoGeneral information about fonts
QFontMetricsFont metrics information
QFontMetricsFFont metrics information
QGlyphRunDirect access to the internal glyphs in a font
QGradientUsed in combination with QBrush to specify gradient fills
QHashTemplate class that provides a hash-table-based dictionary
QHostAddressIP address
QHttp2ConfigurationControls HTTP/2 parameters and settings
QHttpPartHolds a body part to be used inside a HTTP multipart MIME message
QIconScalable icons in different modes and states
QImageHardware-independent image representation that allows direct access to the pixel data, and can be used as a paint device
QJsonArrayEncapsulates a JSON array
QJsonDocumentWay to read and write JSON documents
QJsonObjectEncapsulates a JSON object
QJsonParseErrorUsed to report errors during JSON parsing
QJsonValueEncapsulates a value in JSON
QKeySequenceEncapsulates a key sequence as used by shortcuts
QListTemplate class that provides lists
QLocaleConverts between numbers and their string representations in various languages
QMapTemplate class that provides a red-black-tree-based dictionary
QMimeTypeDescribes types of file or data, represented by a MIME type string
QMultiHashConvenience QHash subclass that provides multi-valued hashes
QMultiMapConvenience QMap subclass that provides multi-valued maps
QNetworkAddressEntryStores one IP address supported by a network interface, along with its associated netmask and broadcast address
QNetworkCacheMetaDataCache information
QNetworkCookieHolds one network cookie
QNetworkInterfaceListing of the host’s IP addresses and network interfaces
QNetworkProxyNetwork layer proxy
QNetworkProxyQueryUsed to query the proxy settings for a socket
QNetworkRequestHolds a request to be sent with QNetworkAccessManager
QOpenGLDebugMessageWraps an OpenGL debug message
QPainterPathContainer for painting operations, enabling graphical shapes to be constructed and reused
QPaletteContains color groups for each widget state
QPenDefines how a QPainter should draw lines and outlines of shapes
QPersistentModelIndexUsed to locate data in a data model
QPicturePaint device that records and replays QPainter commands
QPixmapOff-screen image representation that can be used as a paint device
QPolygonVector of points using integer precision
QPolygonFVector of points using floating point precision
QProcessEnvironmentHolds the environment variables that can be passed to a program
QQueueGeneric container that provides a queue
QRawFontAccess to a single physical instance of a font
QRegExpPattern matching using regular expressions
QRegionSpecifies a clip region for a painter
QRegularExpressionPattern matching using regular expressions
QRegularExpressionMatchThe results of a matching a QRegularExpression against a string
QRegularExpressionMatchIteratorIterator on the results of a global match of a QRegularExpression object against a string
QSetTemplate class that provides a hash-table-based set
QSslCertificateConvenient API for an X509 certificate
QSslCertificateExtensionAPI for accessing the extensions of an X509 certificate
QSslCipherRepresents an SSL cryptographic cipher
QSslConfigurationHolds the configuration and state of an SSL connection
QSslDiffieHellmanParametersInterface for Diffie-Hellman parameters for servers
QSslErrorSSL error
QSslKeyInterface for private and public keys
QSslPreSharedKeyAuthenticatorAuthentication data for pre shared keys (PSK) ciphersuites
QStackTemplate class that provides a stack
QStaticTextEnables optimized drawing of text when the text and its layout is updated rarely
QStorageInfoProvides information about currently mounted storage and drives
QStringUnicode character string
QStringListList of strings
QTextBlockFormatFormatting information for blocks of text in a QTextDocument
QTextBoundaryFinderWay of finding Unicode text boundaries in a string
QTextCharFormatFormatting information for characters in a QTextDocument
QTextCursorOffers an API to access and modify QTextDocuments
QTextDocumentFragmentRepresents a piece of formatted text from a QTextDocument
QTextFormatFormatting information for a QTextDocument
QTextFrameFormatFormatting information for frames in a QTextDocument
QTextImageFormatFormatting information for images in a QTextDocument
QTextListFormatFormatting information for lists in a QTextDocument
QTextTableCellFormatFormatting information for table cells in a QTextDocument
QTextTableFormatFormatting information for tables in a QTextDocument
QUrlConvenient interface for working with URLs
QUrlQueryWay to manipulate a key-value pairs in a URL’s query
QVariantActs like a union for the most common Qt data types
QVectorTemplate class that provides a dynamic array
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值