Qt隐式共享

一、Qt隐式共享介绍


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

​ 共享类由一个指向包含引用计数和数据的共享数据块的指针组成。

​ 当创建共享对象时,它将引用计数设置为1。每当新对象引用共享数据时,引用计数就递增,当对象解引用共享数据时,引用计数就递减。当引用计数变为零时,将删除共享数据。

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

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

​ 隐式共享的好处是程序不需要进行不必要地数据复制操作,从而减少内存的使用和数据的复制。此外,对象可以很容易地被赋值,作为函数参数传递,并从函数中返回。
 

二、代码示例

1.QMap

隐式共享大多发生的背后,编程人员一般不需要关注它们。但是,隐式共享导致Qt的容器类和STL中的容器类有很大的不同。由于隐式共享,当复制一个容器时,它们其实是共享一份数据的。如下代码所示:

QMap aMap;
aMap[0] = "a";
aMap[1] = "b";
QMap::iterator iter = aMap.begin();
QMap bMap = aMap;
iter.value() = "c";

此处,迭代器iter的使用要格外小心,因为它指向了共享数据。如果我们iter.value() = "c";,我们改变的将会是共享的实体,即会影响到两个容器。所以这个时候aMap和bMap都被影响了。

我们将代码稍作修改:

QMap aMap;
aMap[0] = "a";
aMap[1] = "b";
QMap bMap = aMap;
QMap::iterator iter = aMap.begin();
iter.value() = "c";
qDebug() << bMap;

此时,迭代器iter指向的是aMap,并不会修改bMap的数据。

还有一种方式,调用detach接口分离。

QMap aMap;
aMap[0] = "a";
aMap[1] = "b";
QMap::iterator iter = aMap.begin();
QMap bMap = aMap;
bMap.detach();
iter.value() = "c";

以上结果可以发现,只有aMap被改变了,bMap还是原来的值。

2.QString

QString采用隐式共享技术,将深拷贝和浅拷贝很好地结合了起来。

QString str1 = "ubuntu";
QString str2 = str1; //  发生一次浅拷贝 str1 str2指向同一个数据结构,引用计数器值为2
qDebug() << "str1 = " << str1;  // str1 =  "ubuntu"
qDebug() << "str2 = " << str2;  // str2 =  "ubuntu"

// 对 QString对象str2修改导致一次深拷贝,str2指向一个新的与str1指向不同的数据结构其引用计数值为1,str1指向的数据结构引用计数器值减1后为1
str2[2] = 'm';
str2[0] = 'o';
qDebug() << "str1 = " << str1;  // str1 =  "ubuntu"
qDebug() << "str2 = " << str2;  // str2 =  "obmntu"

// str2赋值给str1,str1原指向的数据结构的引用数修改为0,也就是没有对象使用这个数据结构会从内存中释放
// 操作结束后,str1 str2  指向同一个数据结构,引用计数器值为2
str1 = str2;
qDebug() << "str1 = " << str1;  // str1 =  "obmntu"
qDebug() << "str2 = " << str2;  // str2 =  "obmntu"

你可能感兴趣的:(Qt,qt)