Qt——数据的隐式共享

一、隐式共享类

在Qt中有很多隐式共享类( Implicitly Shared Classes ),什么是隐式共享呢,请参考官方文档的说明。

好吧,翻译一下——

许多C++类隐式地共享数据,使得资源使用最大化,以及对象拷贝最小化。隐式共享类在传参时既安全又高效,因为只传了一个指向数据的指针,并且只有给它写入时数据才会被拷贝。

看了概念之后是不是有点眉目了呢?至少明白隐式共享与C++中参数传值/传引用有关。

 

二、山重水复

首先定义一个如下的类

class TestClass
{
public:
	TestClass(int i)
	{
		data = i;
	}
private:
	int data;
};

它有一个整型的数据成员,通过构造函数可以给它传一个整数数据。

然后按照下面的方式定义它的2个对象

TestClass a(5);
TestClass b = a;

此时虽然对象a和b中的数据一样,都是5,但它们各自都有一份整型值的拷贝,这就造成了内存浪费。

 

三、柳暗花明

于是我们想到一个节约内存的方法:

当几个不同对象的数据一样时,保留一份数据就够了;只有当某个对象发生了改变,才用得着一份新的数据。

这涉及到一个新的名词,叫做“引用计数”( reference count )。用一个数来记录有多少对象正在使用同一份数据,当一个新对象被创建,计数加一,当一个对象被销毁,计数减一。如果计数为0,意味着没有对象使用它了,从而将数据释放。

改变上面的程序来简单解释一下——

class DataArea{
public:
	DataArea(int data)
	{
		this->data = data;
		count = 1;
	}
	void increaseRef()
	{
		count++;
	}
	void decreaseRef()
	{
		if (--count == 0)
			delete this;
	}
private:
	int count;
	int data;
};

class TestClass{
public:
	TestClass(int i)
	{
		dataArea = new DataArea(i);
	}
	TestClass(const TestClass &data)
	{
		dataArea = data.dataArea;
		dataArea->increaseRef();
	}
	TestClass &operator=(const TestClass &testObj)
	{
		if (dataArea != testObj.dataArea){
			dataArea->decreaseRef();
			dataArea = testObj.dataArea;
			dataArea->increaseRef();
		}
		return *this;
	}
	~TestClass()
	{
		dataArea->decreaseRef();
	}
private:
	DataArea *dataArea;
};

类TestClass是我们直接使用的类,这个类有一个私有成员指向数据区,如果不同对象有相同的数据,那么它们指向同一个数据区。

且看下面的用法:

TestClass a(5);
TestClass b = a; 
b = TestClass(2);

跟踪一下a和b的数据区域指针有什么变化——

TestClass a(5);

  1.进入类TestClass的构造函数

  2.进入类DataArea的构造函数

  3.count = 1、data = 5 (引用计数为1,数据是5)

TestClass b = a;

  1.进入类TestClass的拷贝构造函数

    b和a中的数据指针指向同一个对象

  2.调用计数增加函数

    count = 2、data = 3

b = TestClass(2);

  1.TestClass(2) —— TestClass的构造函数

  2.类DataArea的构造函数

    count = 1、data = 4

  3.赋值运算符——

    b(同时也是a)的数据区指针调用计数减一函数,a计数变为1

  4.改变b的数据区指针,指向TestClass(2)对象的数据区

    b(同时也是对象TestClass(2))的数据区指针调用计数加一函数

    b的计数变为2

  5.析构TestClass(2)这个对象,进入计数减一函数

    b的计数变为1

当然,程序结束时,会调用a和b的析构函数,计数减为0,释放数据区。

上面这些其实就是我们平时说的“写时复制”(copy on write)。

为了方便理解,上面的例子可能不太好。当一个对象很大很复杂的时候,直接拷贝可能对效率有很大影响,这时就能体现出写时复制的优势了。

 

四、Qt中的隐式共享

以QString为例,QString中有一个 constData() 函数,该函数返回一个指向QString中存储的数据的指针。

QString a("Diao");
QString b = a;
qDebug() << a;
qDebug() << b;
qDebug() << &a;
qDebug() << &b;
qDebug() << a.constData();
qDebug() << b.constData();

a[0] = 'M';
qDebug() << a;
qDebug() << b;
qDebug() << &a;
qDebug() << &b;
qDebug() << a.constData();
qDebug() << b.constData();

结果如下:

 懂了?

posted @ 2016-08-11 22:46  去冰三分糖  阅读(788)  评论(0编辑  收藏  举报