Qt 4开发实践
上QQ阅读APP看本书,新人免费读10天
设备和账号都新为新人

L3.1 字符串类QString

L1 隐式共享

隐式共享又叫做回写复制(copy on write)。当两个对象共享同一份数据时(通过浅拷贝实现数据块的共享),如果数据不改变,不进行数据的复制。而当某个对象需要改变数据时,则执行深拷贝。

程序在处理共享对象时,使用深拷贝和浅拷贝这两种方法复制对象。所谓的深拷贝,就是生成对象的一个完整的复制品;而浅拷贝则是一个引用复制(例如,仅仅复制指向共享数据的指针)。显然,执行一个深拷贝的代价是比较昂贵的,要占有更多的内存和CPU资源;而浅拷贝的效率则很好,它仅仅需要设置一个指向共享数据块的指针以及修改引用计数的值。

隐式共享可以降低对内存和CPU资源的使用,提高程序的运行效率。它使得在函数中(比如参数、返回值)使用值传递更有效率。

QString类采用隐式共享技术,将深拷贝和浅拷贝有机地结合起来。

下面通过一个例子来具体介绍隐式共享是如何工作的。

        QString str1="data";
        QString str2=str1;       //str2="data"
        str2[3]= 'e';           //str2="date",str1="data"
        str2[0]= 'f';           //str2="fate",str1="data"
        str1=str2;

其中:

QString str1="data":初始化一个内容为"data"的字符串。

QString str2=str1:将该字符串对象str1赋值给另一个字符串str2(由QString的拷贝构造函数完成str2的初始化)。

在对str2赋值的时候,将发生一次浅拷贝,导致两个QString对象都指向同一个数据结构。该数据结构除了保存字符串"data"以外,还保存了一个引用计数器,以记录字符串数据的引用次数。在这里因为str1和str2指向同一个数据结构,因此计数器的值为2。

str2[3]= 'e':对QString对象str2的修改,将会导致一次深拷贝,使得str2对象指向一个新的、不同于str1所指的数据结构(该数据结构的引用计数为1,因为只有str2指向这个数据结构),同时修改原来的、str1指向的数据结构,设置它的引用计数为1(此时,只有QString对象str1指向该数据结构)。继而在这个str2指向的、新的数据结构上完成数据的修改。引用计数为1意味着这个数据没有被共享。

str2[0]= 'f':进一步对QString对象str2进行修改,但这个操作不会引起任何形式的拷贝,因为str2指向的数据结构没有被共享。

str1=str2:将str2赋值给str1。此时,str1修改它指向的数据结构的引用计数器的值为0,也就是说没有QString对象再使用这个数据结构了。因此,str1指向的数据结构将会从内存中释放掉。该操作的结果是:QString对象str1和str2都指向字符串为"fate"的数据结构,该数据结构的引用计数为2。

Qt中支持隐式共享的类,还有:

● 所有的容器类。

● QByteArray、QBrush、QPen、QPalette、QBitmap、QImage、QPixmap、QCursor、QDir、QFont和QVariant等。

L2 内存分配策略

QString在一个连续的内存块中保存字符串数据。当字符串的大小不断增长时,QString需要重新分配内存空间,以便足以保存增加的字符串。QString使用的内存分配策略是:

● 每次分配4个字符空间,直到大小为20。

● 在20~4084之间,QString分配的内存块大小以2倍的速度增长。

● 从4084开始,每次以2048个字符大小(4096个字节,即4KB)的步长增长。

举个例子具体说明QString在后台到底是怎么做的:

        QString test()
        {
            QString str;
            for(int i=0;i<9000;++i)
                str.append("a");
            return str;
        }

定义了一个QString栈对象str,然后为它追加9000个字符。根据QString的内存分配策略,这个循环操作将会导致14次内存重分配:4、8、16、20、52、116、244、500、1012、2036、4084、6132、8180、10228。最后的一次内存重分配操作后,QString对象str具有一个10228个Unicode字符大小的内存块(20456字节),其中有9000个字符空间被使用(18000字节)。