隐式内存共享
Many C++ classes in Qt use implicit data sharing to maximize resource usage and minimize copying. Implicitly shared classes are both safe and efficient when passed as arguments, because only a pointer to the data is passed around, and the data is copied only if and when a function writes to it, i.e., copy-on-write
下面是官方文档对于隐式转换说明:
Qt中的许多C++类使用隐式数据共享来最大限度地提高资源使用率并最大限度地减少复制。当作为参数传递时,隐式共享类既安全又高效,因为只有指向数据的指针会被传递,并且只有当函数向数据写入时,数据才会被复制,即写时复制
QT官网:隐式共享
共享类
共享类由指向共享数据块的指针组成,该数据块包含引用计数和数据
创建共享对象时,会将引用计数设置为1。每当新对象引用共享数据时,引用计数就会增加,当对象取消引用共享数据后,引用计数会减少。当引用计数为零时,共享数据将被删除。
qt中所有使用隐式数据共享的类,其赋值运算符**operator=**采用的都是浅拷贝
即只拷贝指针,共享数据块引用加一,在隐式共享类任何修改其数据的成员函数中,它都会在修改数据之前自动分离出一块新的内存空间(当共享数据块中的引用计数为1的时候会直接更改共享数据块,而不是创建新的内存空间),并对新的内存空间进行修改(除容器迭代器)。
推荐浅拷贝使用
一般情况下我们可以默认使用**opertot=**中的浅拷贝即可,这样会优化内存空间和拷贝效率,即使我们想改其中的内容也不需要担心,因为在进行写入的时候共享类就会在写入函数中自动进行内存分离,创建新的共享内存块,修改也只会在新的共享内存块上修改。
如果真的想显式进行深拷贝,需要调用共享类的特定函数,例如:QImage的copy()函数
QImage的bits()和constBits()
两个函数都是返回第一个像素元素地址,bits()返回的是一个变量指针,所以QImage内部当引用计数不为0时会有一次深拷贝,分配一个新的内存空间。constBits()返回的是一个常亮指针,所以QImage内部不会有深拷贝。
Qimage和Qpixmap中的内存共享
QImage和QPixmap类使用隐式数据共享,可以按值传递对象,如果没有修改操作则不会有深拷贝消耗。
1:创建两个QImage,使用=号赋值,未做修改操作则内存地址相同,引用计数为2
2:QImage作为参数,使用值传递,函数内部不对QImage做修改的话,也不会有只拷贝,但共享内存的引用计数会加1。下面使用值拷贝,引用技术变为4,分别为:qimage, qimage1, Test函数内部的行参,局部变量m
3:QPixmap创建时,其内部也有一个共享内存指针,不过使用默认的QPixmap::fromImage()创建时会开辟一个新的空间,可以看到pixmap内存地址和qimage不一样,且引用计数为1。此时会有一次内存拷贝的消耗
4:当我们从QImage转换为QPixmap不想要内存拷贝消耗时,例如频繁的更新图像显示,可以添加标志Qt::ImageConversionFlag::NoFormatConversion。下图可以看到三个变量内存地址相同,引用计数为3。
5:当发生写入或者修改时,就会出现对共享内存的复制(即深拷贝)。例如下面Qpainter对图片进行绘制,绘制之前两个QImage内存相同,绘制开始后则qimage1内存地址发生改变(分配了一个新的空间),qimage引用计数变为1
改变后: