個別のインスタンスのポイントは、参照カウントが少なくとも 1 であるため、shared_ptr
これがスコープ内にある限り、それが指すオブジェクトがまだ存在することを (可能な限り) 保証することです。shared_ptr
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()
ヌルポインターが原因で爆発しないことをどのように知っていますか?
それはすべて、コードの「...」セクションに何があるかによって異なります。最初の '...' で何かを呼び出すと、(コードの別の部分のどこかで)shared_ptr
同じオブジェクトに対して a をクリアするという副作用がありますか? shared_ptr
そして、それがたまたまそのオブジェクトとは別個に残っている唯一のものである場合はどうなるでしょうか? さようならオブジェクト、ちょうどあなたがそれを使おうとしているところで。
したがって、その質問に答えるには 2 つの方法があります。
関数本体でオブジェクトが死なないことを確認するまで、プログラム全体のソースを注意深く調べてください。
パラメータを参照ではなく個別のオブジェクトに戻してください。
ここで適用される一般的なアドバイス: プロファイラーで現実的な状況で製品の時間を測定し、加えたい変更がパフォーマンスに大きな違い。
コメンターJQの更新
これは不自然な例です。意図的に単純化されているため、間違いは明らかです。実際の例では、間違いは実際の詳細の層に隠されているため、それほど明白ではありません。
メッセージをどこかに送信する関数があります。大きなメッセージになる可能性があるためstd::string
、複数の場所に渡されるときにコピーされる可能性のある a を使用するのでは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
前述のように、すべてのコードが簡単な例のように密集しているため、間違いを見つけるのは簡単です。しかし、実際のプログラムでは、相互にポインターを保持する可変オブジェクト間のより複雑な関係があるため、間違いを犯しやすく、間違いを検出するために必要なテスト ケースを作成するのは困難です。
関数が継続的に非 null であることに依存できるようにする簡単な解決策は、既存の への参照に依存するのではなくshared_ptr
、関数が独自の true を割り当てることです。shared_ptr
shared_ptr
欠点は、コピーされた ashared_ptr
がフリーではないことです。「ロックフリー」の実装でさえ、スレッド化の保証を尊重するためにインターロック操作を使用する必要があります。shared_ptr
そのため、を に変更することで、プログラムを大幅に高速化できる場合がありますshared_ptr &
。しかし、これはすべてのプログラムに安全に適用できる変更ではありません。プログラムの論理的な意味を変更します。
の代わりに, および の代わりにstd::string
全体を使用すると、同様のバグが発生することに注意してください。std::shared_ptr<std::string>
previous_message = 0;
メッセージをクリアするために、次のように言いました。
previous_message.clear();
その場合、症状は、未定義の動作ではなく、空のメッセージを誤って送信することになります。非常に大きな文字列の余分なコピーのコストは、 a をコピーするコストよりもはるかに重要になる可能性があるshared_ptr
ため、トレードオフは異なる場合があります。