Qt中的隐式共享机制(implicit sharing)
隐式共享
在使用Qt容器类的时候会可能用到隐式共享机制(implicit sharing),隐式共享又叫做回写复制**(copy on write)**。当两个对象共享同一份数据(通过浅拷贝实现数据块的共享)时,如果数据不改变,则不进行数据的复制。而当某个对象需要改变数据时,则进行深拷贝。程序在处理共享对象时,使用深拷贝和浅拷贝这两种方法复制对象, 而深拷贝要占用更多的内存和CPU资源,隐式共享可以降低对内存和CPU资源的使用率,提高程序的运行效率。它使得在函数(如参数、返回值)中使用值传递更有效率。
Qt中支持隐式共享的类包括:QString、QByteArray、QBrush、QPen、QPalette、QBitmap、QImage、QPixmap、QCursor、QDir、QFont和QVariant等。
例如:
QString str1 = "data"; //初始化一个内容为data的字符串
QString str2 = str1; //(a)
str2[3] = 'e'; //(b)
str2[0] = 'f'; //(c)
str1 = str2; //(d)
其中:
(a) QString str2 = str1; 将该字符串对象str1赋值给另一个字符串str2(由Qtring的复制构造函数完成str2的初始化),此时str2 = “data”。在对str2赋值的时候,将发生一次浅拷贝,导致两个QString都指向同一个数据结构。该数据结构除保存字符串"data"外,还保存了一个引用计时器,以记录字符串数据的引用次数。在这里str1、str2指向同一个数据结构,所以计数器的值为2。
(b) str2[3]=‘e’; 对Qtring对啊str2进行的修改会导致依次深拷贝,使得str2对象指向一个新的、不同于str1所指的数据结构(该数据结构的引用计数为1,因为只有str2指向这个数据结构),同时修改原来的str1指向的数据结构,设置它的引用计数为1(此时只有QString对象str1指向该数据结构)。继而在这个str2指向的、新的数据结构上完成数据的修改。引用计数为1意味着这个数据没有呗共享。此时str2=“date”, str1=“data”。
© str2[0]=‘f’; 进一步对QString对象str2进行修改,但这个操作不会引起任何形式的复制,因为str2指向的数据结构没有被共享。此时str2=“fate”, str1=“data”。
(d) str1=str2; 将str2赋值给str1。此时,str1将它指向的数据结构的引用计时器的值修改为0,也就是说没有QString对象再使用这个数据结构了。因此str1指向的数据结构将会从内存中释放掉。该操作的结果是QString对象str1和str2都指向字符串为"fate"的数据结构,该数据结构的引用计数为2。
再例如:
QPixmap pix1, pix2;
pix1.load("image.jpg");
pix2 = pix1; // pix1和pix2共享数据
QPainter paint;
paint.begin(&pix2); //pix2 自动脱离共享数据,因为调用函数QPainter::begin()时,系统认为画一副图将会改变这幅图。
paint.drawText(0,50, "Hi");
paint.end();
另外需要注意区分[]和at,例如对以下程序:(参考https://blog.csdn.net/u011450537/article/details/41674245)
QList<QString> list1;
list1 << "helianthus";
QList<QString> list2 = list1;
cout << &list1[0] << endl;
cout << &list2[0] << endl;
在这个例子中使用了[]运算子,list1和list2中的数据结构经过了复制,所以并不是共享的。因此最后显示的两个记忆体位置并不相同,但是使用了at()时的情况是一样的:
QList<QString> list1;
list1 << "x";
QList<QString> list2 = list1;
//QList::at(int i) const:Returns the item at index position i in the list. i must be a
//valid index position in the list (i.e., 0 <= i < size())
cout << &(list1.at(0)) << endl;
cout << &(list2.at(0)) < < endl;
所以,在只读的情况下,使用at()方法要比使用[]运算子效率高,因为省去了数据结构的复制成本。