在QMap :: remove之后使用QString导致崩溃

时间:2012-09-19 14:20:20

标签: c++ qt qmap

我有以下代码:

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

1 个答案:

答案 0 :(得分:3)

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

QString是implicitly shared type。每个由QString对象组成的新副本都会增加引擎下的引用计数,当计数变为零时,它将被销毁。

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

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

(注意:你没有说明SomeStruct中的内容。但是如果通过它可以到达一个匹配字符串的实例,当地图值{{1}时会被破坏被销毁,然后传入对SomeStruct这样的字符串的引用可能会导致类似的问题。)

隐含共享投入混合的一件事是,有时它隐藏了这种性质的错误 - 如果没有隐式共享,这将更加明显。然而,它使解决方案“便宜”:当您提取密钥以传入时,将其复制到本地变量实例中......并将变量的const引用传递给此例程。这实际上不会复制数据,但会使userId安全,因为会有一个共享计数使其保持活动状态。

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

(注意:将来,尝试使用Simple, Self Contained, Correct Example格式同时包含添加和删除,它可以让您更容易找到自己的吸烟枪。没有它,我们只能对这个问题进行有根据的猜测......它可能完全是由程序中的其他内容引起的!)