Qt隐式共享机制带来的陷阱

        为了提升性能,Qt框架里面使用了隐式共享技术(基于d-p模式),也可以理解为写时复制技术(COW)。在开发过程中我们一般不需要去关系隐式共享的底层运行机制,但是了解一下还是有必要的。 关于隐式共享,网上有很多相关介绍的文章,这里就不多赘述。写这篇文章的目的,只是记录一下这几天在项目上因隐式共享造成的一个BUG。相较于文档之类的理论只是,结合实际项目中的问题来理解隐式共享,可能会有更深的体会。

        先看一下以下代码:

 //定义一个Data数据结构,包含一个成员x   
 struct Data {
	    int x = 10;
    };
   
 void func() {
    //声明一个Data的链表
	QList myList;   
    
    //给链表添加一个x为10的Data元素
	myList << Data{ 10 };

    //声明一个指向链表第一个元素的引用
	Data& intRef = myList[0];

    //声明一个新的链表myList2,等于myList
	QList myList2 = myList;

    //修改myList第一个元素的x
	myList[0].x = 20;

    //到这里,intRef.x为多少? 10?还是20?
    qDebug() << intRef.x;    //打印10,而非20
}

        qDebug() 打印了intRef.x的值为10! intRef引用了myList[0],然后修改了myList[0].x为20,为什么打印出来intRef.x却为10? 这就是隐式共享造成的结果。

        调用myList2 = myList时,发生了隐式共享,也就是说myList2和myList的d指针(d1)是共享的(不了解d-p机制的同学可以自己查阅相关文档),所以intRef引用了d1中的第一个Data元素。 当给myList[0]复制时,会打破这种共享状态,myList会重新创建一个d对象(d2),然后修改d2中第一个Data元素的x为20。 很显然,这时候intRef指向的是myList2的第一个元素,和myList没有任何关系了。

        再模拟一下我在项目上碰到的BUG:  

 //定义一个Data数据结构,包含一个成员x   
 struct Data {
	    int x = 10;
    };
   
 void func() {
    //声明一个Data的链表
	QList myList;   
    
    //给链表添加一个x为10的Data元素
	myList << Data{ 10 };

    //声明一个指向链表第一个元素的引用
	Data& intRef = myList[0];

    //声明一个新的链表myList2,等于myList
	QList myList2 = myList;

    //修改myList第一个元素的x
	myList[0].x = 20;

    //主动析构myList2
    myList2.~QList();

    //到这里,intRef指向的对象还存在吗?
    intRef.x = 1000;
}

         相较上面的代码,这里加了一行myList2.~QList(),显示调用QList的析构函数。因为intRef引用的是myList2的第一个元素,myList2析构后,链表里面的Data元素也被析构了,所以后面再给intRef.x复制,就可能造成堆栈内存的破坏。

        所以,将引用绑定到一个具有隐式共享(写时复制)的容器对象中的元素时,是比较危险的,一定要注意!

你可能感兴趣的:(QT,qt,开发语言)