【问题标题】:QMap insert QVector<QString> by pointer or value?QMap 通过指针或值插入 QVector<QString>?
【发布时间】:2016-02-01 16:56:34
【问题描述】:

我想建立一个设备地图,以便地图包含:

QString 'DeviceID' 和 QVector 'Command List'

目前我的 QMap 如下:

QMap<QString, QVector<QString> *> devices;

QVector<QString> *pCommands= new QVector<QString>;
//       :
// Fill pCommands with lots of data here
//       :
devices.insert(RadioID, pCommands);

但我想知道这是否真的更好:

QMap<QString, QVector<QString>> devices;

QVector<QString> commands;
//       :
// Fill commands with lots of data here
//       :
devices.insert(RadioID, commands);

我确信我在某处读到 Qt 在复制数据时会做一些非常有效的事情。我没有看到很多人在 Qt 中使用指针,而且我必须通过 QMap 最后删除所有 QVector 似乎很混乱......

我使用的是 c++11,所以也许某种移动语义可以在这里工作?

编辑 我已经修改了代码中的 cmets 以显示向量不为空。 我还要声明,一旦存储数据,我就不需要更改数据。

【问题讨论】:

  • 如果你没有特定的理由使用指针,那么我会避免使用它们。
  • @NathanOliver 试图节省过多的内存 :)
  • 除非您共享它们,否则您将消耗内存。如果您要“共享”它们,那么您应该查看一个共享指针来管理指针。
  • @NathanOliver 不清楚 - 但我的意思是在将矢量放入地图之前,它必须充满数据。我已经更新了问题以明确说明:)
  • 使用容器的全部原因是它们为你做内存管理。它们旨在将值存储在:您感兴趣的类型的值,而不是指针的值。如果容器应该拥有存储在其中的对象,那么将原始指针存储在容器中总是错误的,除非容器旨在为您管理数据生命周期。 C++ 和 Qt 中的标准容器都没有这样做 - 它们用于存储值,而不是指向值的指针(除非它们不拥有指向的数据)。

标签: c++ qt c++11 qmap qvector


【解决方案1】:

没有理由考虑手动分配向量会更好。

当然,你只需要复制一个指针,而不是一个向量,但是一个空向量的复制速度还是很快的。存储对象而不是指针的最大好处是您不需要进行手动内存管理。

我使用的是 c++11,所以也许某种移动语义可以在这里工作?

如果QMap::insert 支持移动语义,并且如果QVector 像它们的标准库对应物一样可移动构造,那么您确实可以将向量移动到地图中。但是移动一个空向量和复制一样快。

如果QMap 具有类似std::mapemplace 函数,那么您甚至可以在不移动的情况下就地构造向量。

我对 Qt 并不熟悉,因此您需要从文档中验证这些详细信息。 Qt 是否支持移动语义并没有改变手动内存管理很痛苦的事实。


编辑:根据documentation QVector 似乎是可移动的,但QMap 不支持移动语义。但是,正如 Arpegius 和文档指出的那样,QVector 会进行写时复制优化,因此只要不修改复制的向量,就不会复制数据。在复制空向量时,这些都不重要。


再次编辑

如果添加的向量充满数据,那么复制确实非常昂贵,除非它保持不变。搬家可以弥补这一点,但QMap 似乎不支持这一点。不过,还有另一个技巧:插入一个空向量,然后 swap 将完整向量与地图中的空向量相结合。

【讨论】:

  • @code_fodder 不幸的是QMap::insert 不使用移动语义,但由于doc.qt.io/qt-5/qvector.html#QVector-3 它避免了整个QVector 的副本,所以这里不需要移动语义。
  • @Arpegius 好吧,当需要修改复制的向量时,仍然需要移动语义——这是我认为 code_fodder 需要做的事情——因为这会触发数据的复制。但是如果你像 code_fodder 那样复制一个空向量,就不需要移动,也不需要 COW :)
  • 对,我没想到他总是插入空向量。
  • 非常感谢您提供的信息。我的问题略有错误,因为这是假设 QVector 将充满数据 - 但我没有复制它。这会改变事情吗?我已经(或将在一分钟内)编辑我的问题...
  • @code_fodder 再次编辑。您可以使用swap 来实现无移动语义的移动。
【解决方案2】:

最简单且非常惯用的方法是:

QMap<QString, QVector<QString>> devices;

// Get the vector constructed in the map itself.
auto & commands = devices[RadioID];
// Fill commands with lots of data here - a bit faster
commands.resize(2);
commands[0] = "Foo";
commands[1] = "Bar";
// Fill commands with lots of data here - a bit slower
commands.append("Baz");
commands.append("Fan");

你会在 Qt 和其他代码中经常看到这种模式,这是最简单的方法,在 C++98 和 C++11 上同样适用 :)

这样您就可以处理地图中已经存在的矢量了。向量本身永远不会被复制。向量的成员被复制,但它们是隐式共享的,因此复制实际上是指针复制和原子引用计数增量。

【讨论】:

  • 感谢 Kuba (+1),如果我没记错的话,引用计数增量是 Qt 特定的行为,所以此解决方案特定于 Qt 容器(即不适用于 c++ STL)?
  • @code_fodder 这里唯一的性能差异将在QStringstd::string 之间。 commands 是对地图中已有向量的引用。您可以使用std::map&lt;std::string, std::vector&lt;QString&gt;&gt;,它的性能与上述完全相同。如果您不想复制字符串,请使用emplace_back 而不是append
  • 我们能否确定对地图的更改不会使参考无效?
  • 当然,您不应该在任何地图修改后使用参考。像对待迭代器一样对待它。当然,您可以随意修改引用所指的地图元素。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-12-29
  • 2021-08-25
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多