目录
一、项目优化背景
在Qt框架中QString字符串 QByteArray字节数组 QImage图像类型 QList QVector Qmap等容器类 QVariant通用容器值 QPen QBrush等绘图相关类都是隐式共享的,即读时共享写时复制,极大的提高了对象的拷贝和存储效率,对于大对象的拷贝等很友好;但是项目中难免有一些自定义的大对象,如果能够享受这种隐式共享的福利也能提升软件的效率。
二、继承QSharedData方式
在Qt框架中可以很方便的让自定义数据继承QSharedData类,进而使用QSharedDataPointer管理数据即可使得数据拥有隐式共享的能力,以下是继承QSharedData的代码举例说明:
2.1 继承QSharedData 定义数据
#include <QSharedData>
class PersonData : public QSharedData {
public:
QString name;
int age;
PersonData() : age(0) {}
PersonData(const QString &n, int a) : name(n), age(a) {}
};
2.2 QSharedDataPointer管理数据
#include <QSharedDataPointer>
class Person {
private:
// 使用隐式共享指针 引用计数管理生命周期 读时共享写时复制
QSharedDataPointer<PersonData> d;
public:
Person() : d(new PersonData) {}
Person(const QString &name, int age) : d(new PersonData(name, age)) {}
// 拷贝构造函数是隐式的,浅拷贝
Person(const Person &other) : d(other.d) {}
// 赋值运算符也是浅拷贝
Person &operator=(const Person &other) {
d = other.d;
return *this;
}
~Person() {}
// 修改数据时自动触发深拷贝(写时复制)
void setName(const QString &name) {
d.detach(); // 如果当前有多个引用者,则复制一份新的数据并d指向该新数据
d->name = name;
}
void setAge(int age) {
d.detach(); // 如果当前有多个引用者,则复制一份新的数据
d->age = age;
}
QString name() const { return d->name; }
int age() const { return d->age; }
};
2.3 测试使用
void TestQSharedData()
{
Person p1("Alice", 25);
Person p2 = p1; // 浅拷贝,共享同一个PersonData
qDebug() << "p1:" << p1.name() << p1.age(); // 输出: Alice 25
qDebug() << "p2:" << p2.name() << p2.age(); // 输出: Alice 25
// 修改p2的数据,此时触发detach()
p2.setAge(30);
qDebug() << "p1 after p2 modification:" << p1.name() << p1.age(); // 输出: Alice 25
qDebug() << "p2 after modification:" << p2.name() << p2.age(); // 输出: Alice 30
}
三、QSharedData原理
隐式共享指针基本原理和共享指针一样,都是基于引用计数判定是否进行实际对象的删除,类似于shared_ptr;只是在此基础上对于对象的修改做了优化,detach操作判定引用计数为大于1时会重新创建对象,即写时复制。
区别于std::shared_ptr若需要两个指针独立时必须进行深拷贝如下:
std::shared_ptr<int> p1 = std::make_shared<int>(10);
std::shared_ptr<int> p2 = std::make_shared<int>(*p1); // 拷贝 p1 指向的值
SharedPointer只需要在修改前detach即可。
基于引用计数的隐式共享实现原理(简化):
基于引用计数的隐式共享实现原理(简化)
template <typename T>
class SharedPointer {
public:
SharedPointer() : data(nullptr), refCount(new int(1)) {}
SharedPointer(const SharedPointer& other)
: data(other.data), refCount(other.refCount) {
++(*refCount);
}
SharedPointer& operator=(const SharedPointer& other) {
if (this != &other) {
if (--(*refCount) == 0) {
delete data;
delete refCount;
}
data = other.data;
refCount = other.refCount;
++(*refCount);
}
return *this;
}
~SharedPointer() {
if (--(*refCount) == 0) {
delete data;
delete refCount;
}
}
T* operator->() {
return data;
}
//前面与共享指针类似 detach基于refcnt进行深拷贝
void detach() {
if (*refCount > 1) {
T* newData = new T(*data);
--(*refCount);
data = newData;
refCount = new int(1);
}
}
private:
T* data;
int* refCount;
};
四、总结
Qt的隐式共享和享元模式提供了以下优势:
内存效率:减少不必要的数据复制
性能优化:值传递变得轻量级
简洁API:无需手动管理引用计数
灵活性:结合const引用可进一步优化性能在实际开发中,应当:优先通过const引用传递隐式共享对象需要修改时使用非const引用返回值时直接返回对象(不要返回指针)注意线程安全限制