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類似の問題。)

暗黙の共有がミックスに投げ込む1つのことは、この性質のバグを隠すことがあるということです。これは、暗黙の共有がなければより明白になります。それでも、ソリューションは「安価」になります。渡すキーを抽出するときに、それをローカル変数インスタンスにコピーし、その変数へのconst参照をこのルーチンに渡します。これは実際にはデータをコピーしませんが、データをuserId存続させる共有カウントがもう1つあるため、安全になります。

これは、より一般的に優れたプロトコルの実装に役立ちます。参照型をルーチンに渡すことは、呼び出している関数の実行時間全体にわたって、参照されるオブジェクトの存続期間を保証できることを意味しますそれが疑わしい場合は、コピーを作成し、代わりにそのコピーへの参照を渡します。

(注:将来的には、追加と削除の両方が含まれているシンプルで自己完結型の正しい例の形式を使用してみてください。これにより、自分で喫煙銃をより簡単に見つけることができます。それがないと、知識に基づいた推測しかできません。問題...それはあなたのプログラムの他の何かによって完全に引き起こされる可能性があります!)

于 2012-09-19T16:30:56.650 に答える