Почему адрес QVector изменяется, если size() не превышает capacity() после вызова QXYSeries::replace()?
У меня есть небольшая тестовая программа, которая вставляет значения в QVector. QVector зарезервирован при запуске, чтобы выделить минимальный объем памяти. Я проверяю, что емкость правильная и что размер QVector не превышает емкость.
Я заметил, что добавление данных в QVector не меняет адрес первого элемента. Все хорошо до сих пор.
Затем я использую метод replace() класса QSplineSeries для копирования данных из вектора в диаграмму. Я снова проверяю адрес первого элемента в 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 Вместимость: 1000
- Адрес первого элемента: 0x222725d61e8
- Размер: 1 Вместимость: 1000
- Адрес первого элемента: 0x222725dc0d8
- Размер: 2 Вместимость: 1000
- Адрес первого элемента: 0x222725dc0d8
- Размер: 2 Вместимость: 1000
- Адрес первого элемента: 0x222725d61e8
- Размер: 3 Вместимость: 1000
- Адрес первого элемента: 0x222725d61e8
- Размер: 3 Вместимость: 1000
- Адрес первого элемента: 0x222725dc0d8
- Размер: 4 Вместимость: 1000
- Адрес первого элемента: 0x222725dc0d8
- Размер: 4 Вместимость: 1000
- Адрес первого элемента: 0x222725d61e8
- Размер: 5 Вместимость: 1000
- Адрес первого элемента: 0x222725d61e8
- Размер: 5 Вместимость: 1000
- Адрес первого элемента: 0x222725dc0d8
Ссылка на исходный код вызова QSplineSeries::replace(QVector) здесь
РЕДАКТИРОВАТЬ
Я просто понял, что оператор присваивания из QVector вызывается в QSplineSeries:: заменить, который выглядит как это. Это означает, что внутренний вектор в QSplineSeries копирует переданный мной QVector, но я все еще не понимаю, почему переданный мной QVector меняет свой адрес.
1 ответ
Вы столкнулись с одним из отличий контейнеров Qt от контейнеров стандартной библиотеки. В частности, Qt использует неявное совместное использование и копирование при записи. Это означает, что при создании копии контейнера и копия, и оригинал указывают на одни и те же данные. Только когда делается попытка изменить содержимое одного из контейнеров, делается глубокая копия. (Изменяемый контейнер получает новый адрес для своих данных, даже если это был исходный контейнер.)
Применим это к вашему примеру. Сначала вы получите статистику по вашемуQVector
. Далее вы звонитеQSplineSeries::replace(QVector<QPointF>)
. Смысл этой функции - скопировать предоставленныйQVector
в данные QSplineSeries
. Эта копия переживет возврат функции. (Параметр также создает копию вектора - он не передается по ссылке - но эта копия уничтожается, когда функция возвращается.) Итак, когда вы переходите к следующей строке,vector
обменивается данными с другим контейнером, в частности с тем, который находится глубоко внутри chart
. Все идет нормально.
Проверка размера и вместимости vector
все еще хорошо. Но тогда вы используете operator[]
. Это возвращает непостоянную ссылку на элемент вектора. Это означает, что вы можете писать на него. Не имеет значения, действительно ли вы пишете ему. У объекта нет возможности узнать, что вы будете делать со ссылкой, поэтому он должен подготовиться к потенциальной записи. Поскольку данные являются общими, запускается глубокая копия. Данные вvector
копируется в новый фрагмент памяти, который вы можете перезаписать, не затрагивая chart
. Следовательно, адрес памяти меняется.
Если вы хотите выполнить анализ, не вызывая глубокую копию, измените &vector[0]
к &vector.at(0)
или чтобы vector.constData()
. Они обеспечиваютconst
-квалифицированный доступ к данным, поэтому они не запускают глубокую копию.
Примечание: версия operator[]
используется const QVector
возвращает постоянную ссылку, поэтому глубокая копия не запускается. Затем на-const
природа vector
здесь фактор.