如何将 QVector<T> 的地址动态存储在另一个 QVector<QVector<T>*> 中?
Posted
技术标签:
【中文标题】如何将 QVector<T> 的地址动态存储在另一个 QVector<QVector<T>*> 中?【英文标题】:How to dynamically store address of a QVector<T> in another QVector<QVector<T>*>? 【发布时间】:2020-10-04 14:20:35 【问题描述】:我正在处理大量数据,例如 100,000 个双精度值,每 100 毫秒左右收集一次。在任何给定时间,我都必须在我的软件中存储 25 个甚至更多这些生成的数据。两者,每次采集的数据长度以及采集次数都因当前情况而异。我不想将我的数据存储为QVector<QVector<double>>
,因为QVector
将数据存储在相邻的内存位置,并且每次将QVector<double>
添加到QVector<QVector<double>>
都会导致整个内容被复制到给定的新位置,从而导致巨大的滞后(特别是如果已经有 20 个QVector<double>
存在并且我正在添加第 21 个)。
我的解决方案是将给定数据存储为QVector<QVector<double>*>
,即每次数据采集我只是将地址存储到QVector
以进行当前采集。但是,我希望它独立存储在QVector<QVector<double>*>
中,即只要我不清除指针,我就可以访问数据。我无法弄清楚如何做到这一点。我尝试了多种方法。假设我在我的 .h 文件中声明 QVector<QVector<double>*> myVec;
并使用一个函数在每次采集时向其添加新数据(以下函数仅用于测试,这就是我在其中创建数据的原因函数):
void MainWindow::addData()
myVec << &QVector<double>(0) // Creating a new empty vector and storing it's location
*myVec[0] << 2.7 << 3.4 << 4.5;
这不起作用,大多数时候它会抛出异常,有几次它只是显示一些垃圾值。
void MainWindow::addData()
QVector<double> tempVec; // Create a temprory vector
tempVec << 2.7 << 3.4 << 4.5;
myVec << &tempVec;
当然,这也不起作用,因为一旦退出 addData()
,tempVec
就会被销毁。
void MainWindow::addData()
QVector<double> tempVec; // Create a temprory vector
tempVec << 2.7 << 3.4 << 4.5;
myVec << &QVector(tempVec);
我认为这会起作用,因为我没有存储tempVec
的地址,而是将tempVec
复制到一个新的QVector
并将其地址分配给myVec
,但它也会引发异常。
有什么方法可以将QVector
纯粹作为指针 存储在另一个QVector
中?
【问题讨论】:
不,对向量的每次添加都不会导致重新分配。一个相当聪明的算法会导致在恒定摊销时间内发生重新分配。 调整 QV 的 QV 大小不会复制所有数据,它只是移动指针。别的东西是你的问题。可能你在复制QV?不要。 @Yakk - Adam Nevraumont 谢谢!是的,我正在将 QV 从我的数据源复制到我的计算算法中,因为我希望它们中的每一个都能够彼此独立地运行。 @Chaitanya 移动它而不是复制它?还是您需要它的两个逻辑副本? @Chaitanya 它存储为指针,但如果你复制它可能会重复。 【参考方案1】:您将地址存储到随后被销毁的对象。这意味着您的指针向量包含悬空指针:
void MainWindow::addData()
myVec << &QVector<double>(0);
// the temporary vector is destroyed here and myVec contains a
// dangling pointer
*myVec[0] << 2.7 << 3.4 << 4.5;
我很惊讶你的编译器竟然接受了这个代码,因为获取临时地址应该是一个编译错误。为临时命名允许它编译,但它并没有更好,因为你仍然得到一个悬空指针:
void MainWindow::addData()
QVector<double> tempVec(0);
myVec << &tempVec;
*myVec[0] << 2.7 << 3.4 << 4.5;
// <-- tempVec is destroyed here, so you get a dangling pointer
如果您存储指向对象的指针,则需要确保无论何时访问该指针时该对象仍然存在。
解决此问题的一种方法是在存储其地址之前为每个向量分配new
。但是,当您不再需要指针时,您需要记住 delete
它。取而代之的是,您可以使用智能指针。 std::unique_ptr
在这种情况下可以完成这项工作,但不幸的是 QVector
无法存储不可复制的元素。所以你需要std::shared_ptr
。这样,您不需要将指针存储在 myVec
之外的任何其他位置:
#include <memory>
QVector<std::shared_ptr<QVector<double>>> myVec;
// ...
void MainWindow::addData()
myVec << std::make_shared<QVector<double>>(0);
*myVec[0].get() << 2.7 << 3.4 << 4.5;
如果您不是绝对需要QVector
而可以使用std::vector
,我建议您这样做,因为它可以很好地存储std::unique_ptr
元素:
#include <memory>
#include <vector>
std::vector<std::unique_ptr<QVector<double>>> myVec;
// ...
void MainWindow::addData()
myVec.push_back(std::make_unique<QVector<double>>(0));
*myVec[0].get() << 2.7 << 3.4 << 4.5;
如果您确实需要存储原始指针并且不能使用智能指针,那么恐怕您需要自己管理指向的对象,方法是使用 new
分配它们,然后在完成后使用 @ 987654337@ 不再需要它,用delete
清理:
QVector<QVector<double>*> myVec;
// ...
void MainWindow::addData()
myVec << new QVector<double>(0);
*myVec[0] << 2.7 << 3.4 << 4.5;
// Cleanup code you need to call when you no longer need myVec
for (auto ptr : myVec)
delete ptr;
请记住,如果您从myVec
中删除一个指针或用不同的指针覆盖它,您还需要删除它。这是乏味且容易出错的。当您忘记执行此操作时,就会泄漏内存。所以我建议改用unique_ptr
路由,它会自动为你删除指针。
最后,在您的情况下,这可能更可取,您可以完全避免使用指针并将向量移动到myVec
,方法是使用std::move()
将其转换为右值。不确定QVector
与移动语义的配合如何,但std::vector
做得很好,在这种情况下,您可以将std::vector
用于“内部”和“外部”向量。作为优化,如果您知道需要存储的双精度数,您可以使用reserve()
预先分配以避免在添加元素时代价高昂的重新分配:
#include <memory>
#include <vector>
std::vector<std::vector<double>> myVec;
// ...
void MainWindow::addData()
std::vector<double> tmpVec;
tmpVec.reserve(amount_of_elements);
tmpVec.push_back(2.4);
tmpVec.push_back(3.7);
// etc until filled
myVec.push_back(std::move(tmpVec));
如果涉及的对象支持移动语义,则移动时不会执行任何分配或复制。如前所述,std::vector
确实如此。
非常重要:在对象被“移出”之后(在本例中为tmpVec
),它处于有效但未指定的状态(通常为空)。
【讨论】:
谢谢!我会试试这些。我不是绝对需要QVector
,所以我也会尝试使用std::vector
,我只需要在存储数据后读取数据,然后在我不使用它时立即清除它。
@Chaitanya 由于在您的情况下性能很重要,我添加了一种使用移动语义来避免完全存储指针并同时摆脱重新分配和副本的方法。
@ Nikos C. 这太棒了!我是 C++ 新手,尚未使用 std::unique_ptr
、std::shared_ptr
或移动语义。我现在将在我的算法中尝试它。非常感谢,这正是我一直在寻找的,因为我尽量避免使用直接指针,而是使用迭代器来访问值。在这种情况下,最好使用移动语义。
@Chaitanya 如果您以前从未处理过移动语义,我推荐 Klaus Iglberger 的两部分 CppCon 2019 演讲:part 1、part 2以上是关于如何将 QVector<T> 的地址动态存储在另一个 QVector<QVector<T>*> 中?的主要内容,如果未能解决你的问题,请参考以下文章
“QVector<QVector<QVector<T>>> 向量”是啥意思?