• 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();

    结果如下:

     懂了?

  • 相关阅读:
    Docker 安装Mysql
    Windows Store APP- C# to get IP Address
    使用eclipse构建maven和发布maven的项目
    eclipse中安装maven的插件
    window下安装和部署maven
    oracle12c的(window版本)安装详解
    Linux配置MySQL
    Linux配置Tomcat
    Linux配置JDK环境变量
    maven创建web项目的报错解决方案
  • 原文地址:https://www.cnblogs.com/hellovenus/p/6275545.html
Copyright © 2020-2023  润新知