c++ - 为什么在调用 QXYSeries::replace() 后,当 size() 尚未超过capacity() 时,QVector 的地址会发生变化?

标签 c++ qt qt5 qtcharts

我有一个小型测试程序,可以将值插入到 QVector 中。 QVector 在启动时被保留,以分配最小量的内存。我验证容量是否正确,并且 QVector 的大小没有超过容量。

我观察到将数据附加到 QVector 不会更改第一个元素的地址。 到目前为止一切都很好。

然后,我使用 QSplineSeries 类的 Replace() 方法将数据从 vector 复制到图表。调用此方法后,我再次检查 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();
}

输出

  1. Size: 1 Capacity: 1000
  2. Address of first element: 0x222725d61e8
  3. Size: 1 Capacity: 1000
  4. Address of first element: 0x222725dc0d8
  5. Size: 2 Capacity: 1000
  6. Address of first element: 0x222725dc0d8
  7. Size: 2 Capacity: 1000
  8. Address of first element: 0x222725d61e8
  9. Size: 3 Capacity: 1000
  10. Address of first element: 0x222725d61e8
  11. Size: 3 Capacity: 1000
  12. Address of first element: 0x222725dc0d8
  13. Size: 4 Capacity: 1000
  14. Address of first element: 0x222725dc0d8
  15. Size: 4 Capacity: 1000
  16. Address of first element: 0x222725d61e8
  17. Size: 5 Capacity: 1000
  18. Address of first element: 0x222725d61e8
  19. Size: 5 Capacity: 1000
  20. Address of first element: 0x222725dc0d8

Link to the source code of the QSplineSeries::replace(QVector) call here

编辑

我刚刚意识到 QVector 的赋值运算符在 QSplineSeries::replace 中被调用,看起来像 this 。这意味着 QSplineSeries 中的内部 vector 复制了我传入的 QVector,但我仍然不明白为什么我传入的 QVector 会改变它的地址。

最佳答案

您已经遇到了 Qt 容器与标准库容器的不同之处之一。具体来说,Qt 使用implicit sharing和写时复制。这意味着当创建容器的拷贝时,拷贝和原始数据都指向相同的数据。仅当尝试更改其中一个容器的内容时​​,才会进行深层复制。 (被更改的容器会获取其数据的新地址,即使它是原始容器。)

让我们将其应用到您的示例中。首先,您获得 QVector 的统计数据。接下来您调用QSplineSeries::replace(QVector<QPointF>) 。这个函数的重点是复制提供的 QVector进入QSplineSeries的数据。该拷贝将在函数返回后继续存在。 (该参数还创建 vector 的拷贝 - 它不是通过引用传递的 - 但该拷贝在函数返回时被销毁。)因此,当您到达下一行时, vector正在与另一个容器共享数据,特别是 chart 深处的容器。到目前为止,一切顺利。

检查 vector 的大小和容量还是不错的。但是然后你使用 operator[] 。这将返回对 vector 元素的非常量引用。这意味着您可以写入它。你是否真的写信给它并不重要。该对象无法知道您将如何处理该引用,因此它必须为潜在的写入做好准备。由于数据是共享的,所以会触发深拷贝。 vector中的数据被复制到新的内存块中,您可以覆盖该内存块而不影响 chart 。因此内存地址发生变化。

如果您想在不触发深层复制的情况下完成分析,请更改 &vector[0]&vector.at(0)或发送至vector.constData() 。这些提供const - 对数据的限定访问,因此它们不会触发深层复制。

注意: operator[] 的版本由 const QVector 使用返回一个常量引用,这样就不会触发深拷贝。非const vector的性质是这里的一个因素。

关于c++ - 为什么在调用 QXYSeries::replace() 后,当 size() 尚未超过capacity() 时,QVector 的地址会发生变化?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57931574/

相关文章:

c++ - X11 中是否可以使用全屏 8 位帧缓冲区,如果可以,我做错了什么?

c++ - 智能指针所有权语义和平等

c++ - 为什么 Allocator::reference 被逐步淘汰?

python - 无法从 PyQt5 连接到 mysql 服务器

qt - QObjectPicker 不适用于自定义几何网格和渲染器

qt - 无法将进度条扩展到状态栏的整个宽度

C++ DLL 调用在消费应用程序中实现的函数

c++ - 在套接字之间发送消息

运行 Qt5 应用程序时出现 Qt 错误消息

linux - 在哪里可以获得更多 qtcreator 小部件?