4

我有以下代码:

class NamedObjectContainer {
    //...
    QMap<QString, SomeStruct> mUsed;
    //...
};

const StoredObject* NamedObjectContainer::use(const QString& name, const QString& userId)
{
    qDebug()<<userId;
    mUsed.remove(userId);
    qDebug()<<userId;
    //...
}

在这里,我试图通过键(userId)从 QMap 中删除元素。元素已正确删除。但令人惊讶的是,它在 QMap::remove 之后打印 userId 会崩溃。

Program received signal SIGSEGV, Segmentation fault.
[Switching to Thread 0xb5b2c6c0 (LWP 24041)]
0xb5fe899c in memcpy () from /lib/i686/nosegneg/libc.so.6
(gdb) where
#0  0xb5fe899c in memcpy () from /lib/i686/nosegneg/libc.so.6
#1  0xb7263246 in QString::append () from /home/osmin/stand_cl_dir/Qt4_x86-linux/lib   /libQtCore.so.4
#2  0xb72b6641 in ?? () from /home/osmin/stand_cl_dir/Qt4_x86-linux/lib/libQtCore.so.4
#3  0xb72b218b in QTextStream::operator<< () from /home/osmin/stand_cl_dir/Qt4_x86-linux/lib/libQtCore.so.4
#4  0xb6524740 in QDebug::operator<< () from /usr/lib/libqxmlrpc.so.1
#5  0xb62b5cc0 in tabexchange::NamedObjectContainer::use (this=0x9e2fb08, name=@0xbffe85e4, userId=@0xa12b780) at namedcontainer.cpp:208

什么会导致问题?我正在使用 Qt 4.4.3

4

1 回答 1

3

要详细说明@TI 的评论...

QString 是一种隐式共享类型。每个由 QString 对象组成的新副本都会增加底层的引用计数,当计数变为零时,它就会被销毁。

这里可能发生的事情是有一个初始化例程,它创建了一个 QString 实例,将其作为键传递,并且映射制作了一个副本。(这并没有复制数据,只是增加了共享计数。)然后初始化例程销毁了它的实例,所以唯一剩下的共享实例是存储在映射中共享计数为 1 的实例。

稍后您可能使用了类似的东西QMap::iterator::key()来获取对映射中字符串键的 const 引用,作为userId. 这不会创建 QString 的任何新实例以添加到共享计数中,而是指向地图拥有的实例。所以当地图放开它时......它被摧毁了,现在userId是一个悬空的参考。

(注意:您没有说 . 中的内容SomeStruct。但是,如果通过它可以达到与键匹配的字符串的实例,该实例将在映射值被破坏时SomeStruct被破坏,那么传入对此类字符串的引用userId可能会导致一个类似的问题。)

隐式共享带来的一件事是,有时它隐藏了这种性质的错误——如果没有隐式共享,这将变得更加明显。然而,它使解决方案“便宜”:当您提取要传递的密钥时,将其复制到局部变量实例中......并将对该变量的 const 引用传递给该例程。这实际上不会复制数据,但它会很userId安全,因为会有一个共享计数保持它的活动。

这有助于实现更普遍的良好协议:将引用类型传递给例程应该意味着您可以保证被引用对象的生命周期对于您正在调用的函数的整个运行时。如果有疑问,请制作副本并传递对副本的引用。

(注意:将来,尝试使用包含添加和删除的简单、自包含、正确的示例格式,它可以导致更容易自己找到确凿证据。没有它,我们只能做出有根据的猜测问题......它可能完全是由您的程序中的其他东西引起的!)

于 2012-09-19T16:30:56.650 回答