Qt隐含共享是如何工作的

最近学习Qt,在深入了解容器类的时候,特意关注了下隐含共享机制,以下为书中原文,最后部分是自己的一些总结。

《C++ GUI Qt4编程》摘选:

隐含共享在后台自动运行,所以我们不必再编写任何代码来促进这个优化过程发生。但弄明白它到底如何工作,的确是一件有益的事情。为此,我们将研究一个例子,看看在它的神秘面纱下面到底发生了什么。这个例子使用了QString,它是众多的隐含共享类之一。

QString str1 = "Humpty";

QString str2 = str1;

我们设置str1为"Humpty"并令str2等于str1。在这一点上,QString的两个对象都指向内存中相同的内部数据结构。与字符数据一起,数据结构保存一个引用技术,以指出有多少QString指向相同的数据结构。因为str1和str2都指向相同的数据,所以引用计数的值为2。

str2[0] = 'D';

当修改str2时,它首先将对数据进行深层赋值,以确保str1和str2指向不同的数据结构,然后才将新数值应用于它所复制的数据。str1的数据("Humpty")的引用计数变为1,且把str2的数据("Dumpty")的引用计数也设为1.引用计数为1表示数据并未被共享。

 1 str2.truncate(4); 

如果再次修改str2,则由于str2数据的引用计数为1,将不会发生数据复制。truncate()函数直接对str2的数据进行操作,导致字符串变为"Dump"。引用计数保持为1.

 1 str1 = str2; 

当将str2赋给str1时,str1的数据的引用计数降为0,这意味着没有一个QString仍在使用"Humpty"数据。这样,这些数据就从内存中释放。两个QString都指向"Dump",现在它的引用计数就是2了。

由于引用计数中的竞争情况,数据共享在多线程程序中通常只是作为一个选项而没有给与关注。使用Qt,这并不是一个问题。在内部,容器类使用汇编语言指令执行基本的引用计数。通过QSharedData和QSharedDataPointer类,Qt的用户也可以使用这项技术。

以下为自己总结的部分:

这就像我们使用智能指针一样,例如shared_ptr<T> 对内存中同一个指向内存单元的对象进行托管。我们在做Qt程序时可以用子类继承父类的方式构造一个共享数据类,并继承了父类QSharedData的引用计数规则,并再构造一个类对继承自共享数据类的子类进行成员方法操作例如:

1 #include <QSharedData>
 2 #include <QString>
 3 
 4 class EmployeeData : public QSharedData
 5 {
 6   public:
 7     EmployeeData() : id(-1) { }
 8     EmployeeData(const EmployeeData &other)
 9         : QSharedData(other), id(other.id), name(other.name) { }
10     ~EmployeeData() { }
11 
12     int id;
13     QString name;
14 };
15 
16 class Employee
17 {
18 public:
19   Employee() { d = new EmployeeData; }
20   Employee(int id, const QString &name) {
21     d = new EmployeeData;
22     setId(id);
23     setName(name);
24   }
25   Employee(const Employee &other)
26         : d (other.d)
27   {
28   }
29   void setId(int id) { d->id = id; }
30   void setName(const QString &name) { d->name = name; }
31 
32   int id() const { return d->id; }
33   QString name() const { return d->name; }
34 
35 private:
36   QSharedDataPointer<EmployeeData> d;
37 };

为此,我们通过构造Employee ep1(1001, "XIAOMING");来创建一个对象,并将Employee ep2 = ep1;进行复制构造,因为Employee内部私有成员是一个包含了EmployeeData的共享数据指针,所以我们的两个对象共用一个,引用计数为2。如果我们对ep2进行了setName的操作,这样ep2就会自我复制一份,并修改内部成员,ep1的引用计数变为1,ep2的引用计数也变为1,这样成员数据不再共享。

以上自己写的尚有不足之处,希望大家发现错误能指正。

相关推荐