为什么调用QXYSeries::replace()后size()还没有超过capacity()时,QVector的地址就变了?
Why does the address of QVector change when size() has not surpassed capacity() after calling QXYSeries::replace()?
我有一个小的测试程序,可以将值插入到 QVector 中。 QVector 在开始时保留,以分配最少的内存。我验证容量是正确的,QVector的大小没有超过容量。
我观察到将数据附加到 QVector 不会更改第一个元素的地址。
到目前为止一切都很好。
然后我使用 QSplineSeries class 的 replace() 方法将数据从矢量复制到图表。调用此方法后,我再次检查 QVector 中第一个元素的地址,令我惊讶的是地址已更改!
每次调用此方法时都会发生这种情况,我想了解为什么会发生这种情况。我怀疑可能与 Qt 容器的隐式共享有关,但我不明白为什么要更改此容器的地址。
示例代码和输出如下所示。
#include <QApplication>
#include <QDebug>
#include <QtCharts/QChart>
#include <QtCharts/QChartView>
#include <QtCharts/QSplineSeries>
#include <memory>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
auto chart = std::make_unique<QtCharts::QChartView>(new QtCharts::QChart());
chart->chart()->addSeries(new QtCharts::QSplineSeries);
QVector<QPointF> vector;
vector.reserve(1000);
while (true) {
vector.append(QPointF(0, 0));
qDebug() << "1. Size: " << vector.size() << " Capacity: " << vector.capacity();
qDebug() << "1. Address of first element: " << &vector[0];
auto series = dynamic_cast<QtCharts::QSplineSeries*>(chart->chart()->series().at(0));
series->replace(vector);
qDebug() << "2. Size: " << vector.size() << " Capacity: " << vector.capacity();
qDebug() << "2. Address of first element: " << &vector[0];
}
return a.exec();
}
输出
- Size: 1 Capacity: 1000
- Address of first element: 0x222725d61e8
- Size: 1 Capacity: 1000
- Address of first element: 0x222725dc0d8
- Size: 2 Capacity: 1000
- Address of first element: 0x222725dc0d8
- Size: 2 Capacity: 1000
- Address of first element: 0x222725d61e8
- Size: 3 Capacity: 1000
- Address of first element: 0x222725d61e8
- Size: 3 Capacity: 1000
- Address of first element: 0x222725dc0d8
- Size: 4 Capacity: 1000
- Address of first element: 0x222725dc0d8
- Size: 4 Capacity: 1000
- Address of first element: 0x222725d61e8
- Size: 5 Capacity: 1000
- Address of first element: 0x222725d61e8
- Size: 5 Capacity: 1000
- Address of first element: 0x222725dc0d8
Link to the source code of the QSplineSeries::replace(QVector) call here
编辑
我刚刚发现 QVector 的赋值运算符在 QSplineSeries::replace 中被调用,看起来像 this。这意味着 QSplineSeries 中的内部向量复制了我传入的 QVector,但我仍然不明白为什么我传入的 QVector 更改了它的地址。
您遇到了 Qt 容器与标准库容器不同的一种方式。具体来说,Qt 使用 implicit sharing 和写时复制。这意味着当创建容器的副本时,副本和原始数据都指向相同的数据。只有当尝试更改其中一个容器的内容时,才会进行深拷贝。 (被更改的容器为其数据获取一个新地址,即使它是原始容器。)
让我们将其应用到您的示例中。首先,您会获得 QVector
的统计数据。接下来你调用 QSplineSeries::replace(QVector<QPointF>)
。此函数的要点是将提供的 QVector
复制到 QSplineSeries
的数据中。此副本将在函数的 return 后继续存在。 (该参数还创建了向量的一个副本——它不是通过引用传递的——但该副本在函数 returns 时被销毁。)所以当你到达下一行时,vector
正在共享数据与另一个容器,特别是 chart
深处的容器。到目前为止,还不错。
检查vector
的大小和容量还是不错的。但是然后你使用 operator[]
。 return 是对向量元素的非常量引用。这意味着你可以写信给它。你是否真的写信给它并不重要。该对象无法知道您将如何处理该引用,因此它必须为可能的写入做好准备。由于数据是共享的,因此会触发深拷贝。 vector
中的数据被复制到一个新的内存块中,您可以在不影响 chart
的情况下覆盖它。因此内存地址发生变化。
如果您想在不触发深层复制的情况下完成分析,请将 &vector[0]
更改为 &vector.at(0)
或 vector.constData()
。这些提供对数据的 const
限定访问,因此它们不会触发深层复制。
注意:const QVector
return使用的operator[]
版本是常量引用,因此不会触发深度复制。 vector
的非 const
性质是这里的一个因素。
我有一个小的测试程序,可以将值插入到 QVector 中。 QVector 在开始时保留,以分配最少的内存。我验证容量是正确的,QVector的大小没有超过容量。
我观察到将数据附加到 QVector 不会更改第一个元素的地址。 到目前为止一切都很好。
然后我使用 QSplineSeries class 的 replace() 方法将数据从矢量复制到图表。调用此方法后,我再次检查 QVector 中第一个元素的地址,令我惊讶的是地址已更改!
每次调用此方法时都会发生这种情况,我想了解为什么会发生这种情况。我怀疑可能与 Qt 容器的隐式共享有关,但我不明白为什么要更改此容器的地址。
示例代码和输出如下所示。
#include <QApplication>
#include <QDebug>
#include <QtCharts/QChart>
#include <QtCharts/QChartView>
#include <QtCharts/QSplineSeries>
#include <memory>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
auto chart = std::make_unique<QtCharts::QChartView>(new QtCharts::QChart());
chart->chart()->addSeries(new QtCharts::QSplineSeries);
QVector<QPointF> vector;
vector.reserve(1000);
while (true) {
vector.append(QPointF(0, 0));
qDebug() << "1. Size: " << vector.size() << " Capacity: " << vector.capacity();
qDebug() << "1. Address of first element: " << &vector[0];
auto series = dynamic_cast<QtCharts::QSplineSeries*>(chart->chart()->series().at(0));
series->replace(vector);
qDebug() << "2. Size: " << vector.size() << " Capacity: " << vector.capacity();
qDebug() << "2. Address of first element: " << &vector[0];
}
return a.exec();
}
输出
- Size: 1 Capacity: 1000
- Address of first element: 0x222725d61e8
- Size: 1 Capacity: 1000
- Address of first element: 0x222725dc0d8
- Size: 2 Capacity: 1000
- Address of first element: 0x222725dc0d8
- Size: 2 Capacity: 1000
- Address of first element: 0x222725d61e8
- Size: 3 Capacity: 1000
- Address of first element: 0x222725d61e8
- Size: 3 Capacity: 1000
- Address of first element: 0x222725dc0d8
- Size: 4 Capacity: 1000
- Address of first element: 0x222725dc0d8
- Size: 4 Capacity: 1000
- Address of first element: 0x222725d61e8
- Size: 5 Capacity: 1000
- Address of first element: 0x222725d61e8
- Size: 5 Capacity: 1000
- Address of first element: 0x222725dc0d8
Link to the source code of the QSplineSeries::replace(QVector) call here
编辑
我刚刚发现 QVector 的赋值运算符在 QSplineSeries::replace 中被调用,看起来像 this。这意味着 QSplineSeries 中的内部向量复制了我传入的 QVector,但我仍然不明白为什么我传入的 QVector 更改了它的地址。
您遇到了 Qt 容器与标准库容器不同的一种方式。具体来说,Qt 使用 implicit sharing 和写时复制。这意味着当创建容器的副本时,副本和原始数据都指向相同的数据。只有当尝试更改其中一个容器的内容时,才会进行深拷贝。 (被更改的容器为其数据获取一个新地址,即使它是原始容器。)
让我们将其应用到您的示例中。首先,您会获得 QVector
的统计数据。接下来你调用 QSplineSeries::replace(QVector<QPointF>)
。此函数的要点是将提供的 QVector
复制到 QSplineSeries
的数据中。此副本将在函数的 return 后继续存在。 (该参数还创建了向量的一个副本——它不是通过引用传递的——但该副本在函数 returns 时被销毁。)所以当你到达下一行时,vector
正在共享数据与另一个容器,特别是 chart
深处的容器。到目前为止,还不错。
检查vector
的大小和容量还是不错的。但是然后你使用 operator[]
。 return 是对向量元素的非常量引用。这意味着你可以写信给它。你是否真的写信给它并不重要。该对象无法知道您将如何处理该引用,因此它必须为可能的写入做好准备。由于数据是共享的,因此会触发深拷贝。 vector
中的数据被复制到一个新的内存块中,您可以在不影响 chart
的情况下覆盖它。因此内存地址发生变化。
如果您想在不触发深层复制的情况下完成分析,请将 &vector[0]
更改为 &vector.at(0)
或 vector.constData()
。这些提供对数据的 const
限定访问,因此它们不会触发深层复制。
注意:const QVector
return使用的operator[]
版本是常量引用,因此不会触发深度复制。 vector
的非 const
性质是这里的一个因素。