QSharedDataPointer似乎是在复制对象,而不是增加引用计数
我一直在跟随关于QSharedDataPointer的教程,发现当对包含QSharedDataPointer的 Employee调用拷贝构造函数时,QSharedDataPointer构造器p{other.p} 在某种程度上似乎调用了EmployeeData(QSharedData的子类)的拷贝构造函数,导致QSharedData对象被复制了一份,而不是简单地给QSharedData增加引用计数。
我对在调用QSharedDataPointer拷贝构造函数p{other.p} 时到底发生了什么,以及它是如何链式调用EmployeeData(const EmployeeData& other) 的感到困惑?
谢谢
#include <QtCore/QSharedData>
#include <QtCore/QSharedDataPointer>
#include <QtCore/QString>
#include <QtCore/QDebug>
class EmployeeData: public QSharedData {
public:
int e_id = -1;
QString e_name;
EmployeeData(): e_id{-1} {
qInfo()<<"Default EmployeeData created.";
};
EmployeeData(int id, const QString& name): e_id(id), e_name(name) {
qInfo()<<"EmployeeData "<<e_id<<": "<<e_name<<" created.";
}
EmployeeData(const EmployeeData& other): QSharedData(other), e_id(other.e_id), e_name(other.e_name) {
qInfo()<<"EmployeeData "<<e_id<<": "<<e_name<<" copied.";
}
~EmployeeData() {
qInfo()<<e_id<<": "<<e_name<<" deconstructed.";
}
};
class Employee {
QSharedDataPointer<EmployeeData> p;
public:
Employee() {
p = new EmployeeData();
}
Employee(int id, const QString& name) {
p = new EmployeeData(id, name);
}
Employee(const Employee& other): p{other.p} {
qInfo()<<"Copy QSDPointer for "<<p->e_id<<": "<<p->e_name;
}
~Employee() {
qInfo()<<"SmartPointer "<<p->e_id<<", "<<p->e_name<<", destroyed";
}
};
当用下面这个测试函数进行测试时
void test_e() {
Employee e1 (1, "Rupert");
Employee e2 (2, "Albrecht");
Employee e3 = e1;
Employee e4 = e2;
}
int main() {
test_e();
return 0;
}
它给出了以下日志:
EmployeeData 1: "Rupert" 已创建。
EmployeeData 2: "Albrecht" 已创建。
EmployeeData 1: "Rupert" 已拷贝。
Copy QSDPointer for 1: "Rupert"
EmployeeData 2: "Albrecht" 已拷贝。
Copy QSDPointer for 2: "Albrecht"
SmartPointer 2, "Albrecht" 已销毁
2: "Albrecht" 已析构。
SmartPointer 1, "Rupert" 已销毁
1: "Rupert" 已析构。
SmartPointer 2, "Albrecht" 已销毁
2: "Albrecht" 已析构。
SmartPointer 1, "Rupert" 已销毁
1: "Rupert" 已析构。
It
解决方案
问题出在你访问指针的方式。看看拷贝构造函数
Employee(const Employee& other): p{other.p} {
qInfo()<<"Copy QSDPointer for "<<p->e_id<<": "<<p->e_name;
}
你访问 p->e_id。在这个上下文中,p 是一个可变的 QSharedDataPointer,这意味着对它解引用会得到对象的可变引用。智能指针无法知道你是想通过该对象引用进行读取还是写入。因此它必须为了实现写时复制语义而进行拷贝。
这在 文档中有说明:
T *QSharedDataPointer::operator->()
提供对共享数据对象成员的访问。此函数调用detach()。
经验法则:
- 除非你想要产生拷贝,否则不要在非常量方法中访问
QSharedDataPointer - 对于来自非常量方法的常量访问,要么调用
pointer.constData(),要么使用常量引用 - 尽可能将指针本身声明为const;也就是说如果你从不需要对数据进行非常量访问,且不需要移动构造或赋值(例如在
QObjects中,因为那些本来就不可移动) - 如需更好的控制,请使用
QExplicitlySharedDataPointer
你的构造函数应该像这样:
Employee(const Employee& other): p{other.p} {
const auto& p = this->p;
qInfo()<<"Copy QSDPointer for "<<p->e_id<<": "<<p->e_name;
}
在析构函数中你也有同样的问题。
~Employee() {
const auto& p = this->p;
qInfo()<<"SmartPointer "<<p->e_id<<", "<<p->e_name<<", destroyed";
}