個別のshared_ptr
インスタンスのポイントは、これshared_ptr
がスコープ内にある限り、参照カウントが少なくとも1であるため、ポイントするオブジェクトが存在することを(可能な限り)保証することです。
Class::only_work_with_sp(boost::shared_ptr<foo> sp)
{
// sp points to an object that cannot be destroyed during this function
}
したがって、への参照を使用すると、shared_ptr
その保証が無効になります。したがって、2番目のケースでは:
Class::only_work_with_sp(boost::shared_ptr<foo> &sp) //Again, no copy here
{
...
sp->do_something();
...
}
sp->do_something()
nullポインターが原因で爆発しないことをどのようにして知っていますか?
コードの「...」セクションの内容によって異なります。最初の '...'の間に、shared_ptr
同じオブジェクトへのをクリアするという副作用(コードの別の部分のどこか)があるものを呼び出すとどうなりますか?そして、それがshared_ptr
そのオブジェクトに唯一残っている唯一の場合はどうでしょうか?さようならオブジェクト、それを試して使用しようとしているところです。
したがって、その質問に答えるには2つの方法があります。
関数本体の中でオブジェクトが死なないことが確実になるまで、プログラム全体のソースを注意深く調べてください。
パラメータを変更して、参照ではなく個別のオブジェクトにします。
ここで適用される一般的なアドバイス:プロファイラーで現実的な状況で製品の時間を測定し、加えようとする変更がもたらす変更を決定的に測定するまで、パフォーマンスのためにコードに危険な変更を加えないでください。パフォーマンスに大きな違い。
コメント投稿者JQの更新
これが不自然な例です。それは意図的に単純なので、間違いは明らかです。実際の例では、間違いは実際の詳細のレイヤーに隠されているため、それほど明白ではありません。
どこかにメッセージを送る機能があります。これは大きなメッセージになるstd::string
可能性があるため、複数の場所に渡されるときにコピーされる可能性のあるを使用するのではshared_ptr
なく、文字列にa を使用します。
void send_message(std::shared_ptr<std::string> msg)
{
std::cout << (*msg.get()) << std::endl;
}
(この例では、コンソールに「送信」するだけです)。
次に、前のメッセージを記憶する機能を追加します。次の動作が必要です。最後に送信されたメッセージを含む変数が存在する必要がありますが、メッセージが現在送信されている間は、前のメッセージが存在しない必要があります(変数は送信前にリセットする必要があります)。そこで、新しい変数を宣言します。
std::shared_ptr<std::string> previous_message;
次に、指定したルールに従って関数を修正します。
void send_message(std::shared_ptr<std::string> msg)
{
previous_message = 0;
std::cout << *msg << std::endl;
previous_message = msg;
}
したがって、送信を開始する前に現在の以前のメッセージを破棄し、送信が完了した後で新しい以前のメッセージを保存できます。すべて良い。ここにいくつかのテストコードがあります:
send_message(std::shared_ptr<std::string>(new std::string("Hi")));
send_message(previous_message);
そして、予想通り、これはHi!
2回印刷されます。
ねえ、とそのパラメータは:今すぐに沿ってコードを見て、思っ氏メンテナ、来るsend_message
ですがshared_ptr
。
void send_message(std::shared_ptr<std::string> msg)
明らかに次のように変更できます。
void send_message(const std::shared_ptr<std::string> &msg)
これによりもたらされるパフォーマンス強化について考えてみてください。(通常、大きなメッセージをいくつかのチャネルを介して送信しようとしているため、パフォーマンスの向上は非常に小さいため、測定不可能です。)
しかし、実際の問題は、テストコードが未定義の動作を示すことです(Visual C ++ 2010デバッグビルドでは、クラッシュします)。
メンテナー氏はこれに驚いていますがsend_message
、問題の発生を阻止するために防御チェックを追加しています:
void send_message(const std::shared_ptr<std::string> &msg)
{
if (msg == 0)
return;
しかし、もちろん呼び出されたmsg
ときにnullになることはないので、それはまだ先に進んでクラッシュしますsend_message
。
私が言うように、些細な例ではすべてのコードが非常に接近しているため、間違いを見つけるのは簡単です。しかし、実際のプログラムでは、相互にポインタを保持する可変オブジェクト間の関係がより複雑であるため、間違いが起こりやすく、必要なテストケースを作成して間違いを検出することは困難です。
簡単な解決策は、関数がshared_ptr
ずっとnullでないことを信頼できるようにしたい場合shared_ptr
、既存のへの参照に依存するのではなく、関数が独自のtrueを割り当てることshared_ptr
です。
欠点は、コピーされたa shared_ptr
がフリーではないことです。「ロックフリー」の実装でさえ、インターロックされた操作を使用してスレッド化の保証を守る必要があります。したがって、a shared_ptr
をに変更することで、プログラムを大幅に高速化できる場合がありますshared_ptr &
。ただし、これはすべてのプログラムに安全に加えることができる変更ではありません。プログラムの論理的な意味を変更します。
std::string
全体の代わりにstd::shared_ptr<std::string>
、および次の代わりに使用した場合も、同様のバグが発生することに注意してください。
previous_message = 0;
メッセージをクリアするために、私たちは言った:
previous_message.clear();
次に、症状は、未定義の動作ではなく、空のメッセージを誤って送信することになります。非常に大きな文字列の余分なコピーのコストは、をコピーするコストよりもはるかに重要になる可能性があるshared_ptr
ため、トレードオフは異なる場合があります。