Qt中的隐式共享(Implicit Sharing)

Qt中的隐式共享(Implicit Sharing)

Qt中的许多c++类使用隐式数据共享来最大化资源使用和最小化复制。隐式共享类作为参数传递时既安全又有效,因为只传递一个指向数据的指针,而且只有当函数写入数据时才会复制数据,即写时复制。

1 共享类由指向共享数据块的指针组成,共享数据块包含引用计数和数据.

2 当创建共享对象时,它将引用计数设置为1。每当有新对象引用共享数据时,引用计数增加,当对象解除引用共享数据时,引用计数减少。当引用计数为零时,共享数据将被删除.

3 在处理共享对象时,有两种复制对象的方法。我们通常会说深拷贝浅拷贝深拷贝意味着复制对象浅拷贝是一个引用拷贝,也就是说,只是一个指向共享数据块的指针。从内存和CPU的角度来看,进行深度复制可能是昂贵的。创建浅拷贝非常快,因为它只涉及设置指针和增加引用计数。

4 隐式共享对象的对象赋值(使用operator=())是使用浅拷贝实现的。

5 共享的好处是,程序不需要重复不必要的数据,从而减少内存使用和数据复制。对象可以很容易地被赋值、作为函数参数发送和从函数返回。

6 隐式共享大多发生在幕后;程序员很少需要担心它。然而,Qt的容器迭代器的行为与STL的不同。隐式共享迭代器问题。

7 在多线程应用程序中,会发生隐式共享,详见线程和隐式共享类。

8 当实现你自己的隐式共享类时,使用QSharedData和QSharedDataPointer类。

隐式共享迭代器问题

隐式共享对 STL 样式的迭代器有另一个后果:当迭代器在该容器上处于活动状态时,您应该避免复制该容器。迭代器指向一个内部结构,如果你复制一个容器,你应该非常小心你的迭代器。例如:

QVector<int> a, b;
a.resize(100000);  创建一个填充为 0 的大向量

QVector<int>::iterator i = a.begin();
//  使用迭代器 i 的错误方式:
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 这可能会崩溃。
*/

隐式共享细节

如果对象即将更改且引用计数大于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
     }
 } 

隐式共享类列表

如果一个对象即将被更改,下面列出的类将自动与公共数据分离。程序员甚至不会注意到对象是共享的。因此,您应该将它们的不同实例视为不同的对象。它们总是表现为独立的对象,但在可能的情况下还可以共享数据。因此,可以按值将这些类的实例作为参数传递给函数,而不必担心复制开销

例如:

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

    QString s1 = "ABC";
    
    QString s2 = s1;//浅拷贝
    qDebug() << s1.constData();
    qDebug() << s2.constData();
    
    s2[0] = 'B';	//深拷贝,写时复制
    qDebug() << s1.constData();
    qDebug() << s2.constData();

    return a.exec();
}
QBitArray
QBitmap
QBrush
QByteArray
QByteArrayList
QCache
QCollator
QCollatorSortKey
QCommandLineOption
QContiguousCache
QCursor
QDBusPendingCall
QDBusUnixFileDescriptor
QDateTime
QDebug
QDir
QDnsDomainNameRecord
QDnsHostAddressRecord
QDnsMailExchangeRecord
QDnsServiceRecord
QDnsTextRecord
QFileInfo
QFont
QFontInfo
QFontMetrics
QFontMetricsF
QGlyphRun
QGradient
QHash
QHostAddress
QHttpPart
QIcon
QImage
QJsonArray
QJsonDocument
QJsonObject
QJsonParseError
QJsonValue
QKeySequence
QLinkedList
QList
QLocale
QMap
QMimeType
QMultiHash
QMultiMap
QNetworkAddressEntry
QNetworkCacheMetaData
QNetworkConfiguration
QNetworkCookie
QNetworkInterface
QNetworkProxy
QNetworkProxyQuery
QNetworkRequest
QOpenGLDebugMessage
QPainterPath
QPalette
QPen
QPersistentModelIndex
QPicture
QPixmap
QPolygon
QPolygonF
QProcessEnvironment
QQueue
QRawFont
QRegExp
QRegion
QRegularExpression
QRegularExpressionMatch
QRegularExpressionMatchIterator
QSet
QSslCertificate
QSslCertificateExtension
QSslCipher
QSslConfiguration
QSslDiffieHellmanParameters
QSslError
QSslKey
QSslPreSharedKeyAuthenticator
QStack
QStaticText
QStorageInfo
QString
QStringList
QTextBlockFormat
QTextBoundaryFinder
QTextCharFormat
QTextCursor
QTextDocumentFragment
QTextFormat
QTextFrameFormat
QTextImageFormat
QTextListFormat
QTextTableCellFormat
QTextTableFormat
QUrl
QUrlQuery
QVariant
QVector

你可能感兴趣的:(c++,QT/QML,qt)