【问题标题】:Is it safe to have pointers to elements in Data Structures? (c++ with QT)在数据结构中拥有指向元素的指针是否安全? (c++ 与 QT)
【发布时间】:2016-04-28 15:00:16
【问题描述】:

我正在开发的软件具有以下结构:

ClassA:
QHash<int, ClassB>

ClassB:
QHash<int, ClassC>

ClassC:
QMap<ID, QSharedPointer<ClassD> > (this is because I need to have the items ordered)
QHash<int, QSharedPointer<ClassD> > (this exists so I can access an item via id)

我的问题是,拥有一个指向数据结构内元素的指针是否安全,该指针将被编辑。我在尝试调试时遇到错误,其中调试器无法在断点处停止并且出现 SIGTRAP 错误,但我不确定这是否与内存问题有关。

举一个更好的例子,与我正在开发的软件相关,我有一个代表视频列表的QHash&lt;int, Take&gt;。用户一次只能编辑一个视频,所以我有一个指向当前视频的指针,它是哈希内的一个 Take。每个 Take 都有一堆可以编辑的参数,但最常见的是 Notes 的 QMap。做这样的事情安全吗?

Take *currentTake = &takes[currentTakeId];

----//---

currentTake->addNote(new Note());
currentTake->changeSomeParameter();
etc

【问题讨论】:

  • 也许如果您描述您的任务而不是您开发的当前解决方案,那么有人将能够找到更好的解决方案?我的意思是:您可以将实际任务描述添加到问题文本中吗?
  • 维护指针在数据结构中与其他任何地方没有太大区别。您必须确保在您的过程中没有人删除您的指针,或者使用QPointerQSharedPointer 并对其进行测试。您的问题可能更多与设计有关:清楚地了解对象的生命周期,避免使用全局变量,在有意义时使用引用(当您确定对象至少与引用变量一样长时)。
  • @AlexanderVX 我不是 100% 确定我完全理解你的意思。我可以添加更详尽的描述,但我想让事情尽可能简单,并使问题更广泛。也许我没有很好地解释自己,但我想我的问题是:“在 QT 内部有一个指针(将被编辑,如它所指向的类中的值将被更改)是否安全容器(在这种情况下,我主要使用 QHash 和 QMap,虽然也将使用 QList)。我确保指针在仍在使用时不会被删除。
  • @ymoreau 是的,当引用仍在使用时,我永远不会删除对象。我想我的问题是,在 QMap、QHash、任何数据结构、何时编辑该元素时,是否绝对安全。例如。拥有 ClassB *element 并执行 element->add(ClassC) (或对该指针的任何其他更改)是否安全?我将更改问题以使其更清楚。

标签: c++ qt pointers memory


【解决方案1】:

保留指向集合元素的指针/引用是否安全(或多长时间)取决于该集合。例如,std::vector 在重新分配时使所有指向它的指针无效,而没有重新分配的删除/插入会使插入/删除点之外的所有指针无效(嗯,改变它们指向的内容)。另一方面,std::list 是稳定的;仅当指针指向的特定元素被删除时,指针才会失效。

一个集合通常应该记录它的失效行为。不幸的是,Qt 集合没有。阅读他们的文档告诉我们QMap 是一个红黑平衡二叉树,QHash 是一个单独的链式哈希表,因此它们都应该具有std::list 的失效行为,但是没有保证 的那个。 (例如,QHash 可以将头条目直接存储在桶列表中,这意味着重新散列会使大多数指针无效,而删除一个元素可能会使指向同一桶中元素的指针无效。)

【讨论】:

  • 这是可以轻松测试的东西吗?也就是说,它是安全的还是不安全的,它会在插入新数据/删除后更改指针中的内容时使应用程序崩溃?我自己做了一个快速测试,我添加了 3 个片段,然后选择了 2 个作为当前片段并删除了第一个片段。然后继续编辑当前的(拍摄 2),一切似乎都正常。
  • Qt 是开源的,所以通过代码检查可以很容易地发现它。查看QMapQHash,它们都是安全的(github.com/qtproject/qtbase/blob/dev/src/corelib/tools/… 表明没有什么特别的事情发生,只是每个存储桶中都有一个普通的单链表)。当然,你必须重新检查每次 Qt 升级,看看它们是否改变了任何东西。
【解决方案2】:

使用引用,您示例的数据容器正在存储对象的值,并在您执行takes[currentTakeId] 时返回对该对象的引用。 你可以这样做

Take &currentTake = takes.value(currentTakeId]); 
// same as 'takes[currentTakeId]'
// but avoid to create empty element if 'currentTakeId' has no element

----//---

currentTake.addNote(new Note());
currentTake.changeSomeParameter();

// do not change 'takes' as long as you use 'currentTake'

您的想法很好(或多或少相同),您可以在使用此指针时检索地址并处理指针只要容器未被修改。因为如果调整大小,数据容器可能会复制您的元素,或者删除它,然后指针将无效。

如果您的对象 Take 非常大,则在调整容器大小或按值传递(和复制)时,您可能会因为复制而失去性能。那么存储指针可以是一个解决方案,或者在你的数据类上使用 Qt 的隐式共享模式。

【讨论】:

  • 但是函数 .value 返回一个 const 对象。你不能引用它。我使用指针而不是引用的原因是因为我不能将引用存储为类变量。编辑不仅仅发生在功能上。
猜你喜欢
  • 2017-01-04
  • 1970-01-01
  • 2021-02-02
  • 1970-01-01
  • 2015-09-11
  • 1970-01-01
  • 1970-01-01
  • 2021-12-03
  • 2021-07-14
相关资源
最近更新 更多