C ++-std :: shared_ptrまたはboost :: shared_ptrに参照を渡す


115

で動作する必要がある関数がある場合shared_ptr、(shared_ptrオブジェクトのコピーを避けるために)関数への参照を渡す方が効率的ではないでしょうか?考えられる悪い副作用は何ですか?私は次の2つのケースを想定しています。

1)関数内では、次のように引数のコピーが作成されます

ClassA::take_copy_of_sp(boost::shared_ptr<foo> &sp)  
{  
     ...  
     m_sp_member=sp; //This will copy the object, incrementing refcount  
     ...  
}  

2)関数内では、引数は次のようにのみ使用されます

Class::only_work_with_sp(boost::shared_ptr<foo> &sp) //Again, no copy here  
{    
    ...  
    sp->do_something();  
    ...  
}  

どちらの場合も、参照ではなくboost::shared_ptr<foo>by値を渡す正当な理由がわかりません。値による受け渡しは、コピーのために「一時的に」参照カウントをインクリメントし、関数スコープを終了するときにそれをデクリメントします。私は何かを見落としているか?

明確にするために、いくつかの回答を読んだ後、私は時期尚早の最適化の懸念に完全に同意します。私の質問は、純粋に技術的なコードの観点からのものでした。


質問のタグを変更できるかどうかはわかりませんが、そこにBoostタグを追加してみてください。この質問を探してみましたが、boostタグとsmart-pointerタグを探したため、何も見つかりませんでした。だから、私は自分の質問を作成した直後にあなたの質問を見つけました
エジソン・グスタボ・ムエンツ2009年

回答:


113

個別の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つの方法があります。

  1. 関数本体の中でオブジェクトが死なないことが確実になるまで、プログラム全体のソースを注意深く調べてください。

  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ため、トレードオフは異なる場合があります。


16
渡されたshared_ptrは、呼び出しサイトのスコープに既に存在します。この質問のコードがぶら下がっているポインターが原因で爆破する複雑なシナリオを作成できるかもしれませんが、参照パラメーターよりも大きな問題があると思います!
Magnus Hoff

10
メンバーに保存される場合があります。そのメンバーをクリアするために起こる何かを呼び出すことができます。smart_ptrの重要なポイントは、コールスタックをネストする階層またはスコープのライフタイムを調整する必要がないようにすることです。そのため、このようなプログラムではライフタイムがそうしないと想定するのが最善です。
Daniel Earwicker、2008年

7
それは本当に私の視点ではありません!私が言っていることが私のコードで行う特定のことであると思うなら、あなたは私を理解していないかもしれません。最初にshared_ptrが存在する理由の避けられない影響について話しています。多くのオブジェクトのライフタイムは単に関数呼び出しに関連しているわけではありません。
Daniel Earwicker 08年

8
@DanielEarwickerはあなたのすべてのポイントに完全に同意し、反対のレベルに驚いています。あなたの懸念をさらに関連性のあるものにするのはスレッドです。これが関係するようになると、オブジェクトの有効性についての保証がはるかに重要になります。いい答えだ。
radman 2013年

3
少し前に、共有ポインタへの参照を渡すことに起因する非常に深刻なバグを追跡しました。コードはオブジェクトの状態変化を処理しており、オブジェクトの状態が変化したことに気づくと、それを前の状態のオブジェクトのコレクションから削除し、新しい状態のオブジェクトのコレクションに移動しました。削除操作により、オブジェクトへの最後の共有ポインターが破棄されました。メンバー関数は、コレクション内の共有ポインターへの参照で呼び出されました。ブーム。ダニエル・アーウィッカーは正しいです。
David Schwartz 2013年

115

私は自分が最高の投票結果に同意しないことに気付いたので、専門家の意見を探しに行きました。http://channel9.msdn.com/Shows/Going+Deep/C-and-Beyond-2011-Scott-Andrei-and-Herb-Ask-Us-Anythingから

ハーブサッター:「shared_ptrsを渡すと、コピーは高価です」

スコット・マイヤーズ:「値で渡すか、参照で渡すかに関しては、shared_ptrに特別なことは何もありません。他のユーザー定義型に使用するのとまったく同じ分析を使用してください。Shared_ptrが何らかの方法で解決するという認識は人々にあるようですすべての管理上の問題、そしてそれは小さいので、値で渡すことは必然的に安価です。コピーする必要があり、それに関連するコストがあります...値で渡すことはコストがかかるので、私のプログラムでは適切なセマンティクスでそれを使用し、代わりにconstまたはreferenceへの参照で渡します」

ハーブサッター:「常にconstへの参照によってそれらを渡します。呼び出したものが参照から得たものを変更する可能性があることを知っているために、場合によっては値によって渡す可能性があります...それらをパラメーターとしてコピーすると、ああそれが生きているため、参照カウントを増やす必要はほとんどありません。参照で渡す必要があるので、それを実行してください。」

更新:ハーブはこれをここで拡張しました:http : //herbsutter.com/2013/06/05/gotw-91-solution-smart-pointer-parameters/、ただし、この話の教訓は、あなたが通過するべきではないということですshared_ptrsは、「所有権の共有や譲渡など、スマートポインタ自体を使用または操作する場合を除いて」です。


8
素敵な発見!このテーマに関する一流の専門家2人がSOに関する従来の知恵を公に論駁するのは素晴らしいことです。
ステファントルクスドルフ

3
「値で渡すか、参照で渡すかについては、shared_ptrに特別なことは何もありません」-私はこれに本当に同意しません。それは特別です。個人的には、安全にプレーし、わずかなパフォーマンスの打撃を受けたいと思っています。最適化する必要があるコードの特定の領域がある場合は、const refによるshared_ptrパスのパフォーマンス上の利点を確認します。
JasonZ

3
また、shared_ptrsの過剰使用については合意がありましたが、値渡しと参照渡しの問題については合意がありませんでした。
Nicol Bolas

2
スコット・マイヤーズ:「プログラムの適切なセマンティクスでそれを回避できる場合...」つまり、私の回答にまったく矛盾していません。これは、パラメーターを変更const &することでセマンティクスに影響するかどうかを判断することは非常に簡単なことです。単純なプログラム。
Daniel Earwicker

7
ハーブ・サッター:「たぶん、あなたが何を呼んだかがあなたが参照を得たものを変更するかもしれないことを知っているためかもしれません」。繰り返しになりますが、マイナーケースの場合は少し免除されるので、私の回答と矛盾しません。問題は残っています:const refを使用することが安全であることをどのようにして知っています?単純なプログラムで証明するのは非常に簡単ですが、複雑なプログラムではそれほど簡単ではありません。しかし、ねえ、これ C ++なので、他のほとんどすべてのエンジニアリング上の懸念よりも早期のマイクロ最適化を優先しますよね?!:)
Daniel Earwicker

22

あなたとあなたが一緒に働く他のプログラマーがあなたが本当に何をしているのか本当に知っているのでない限り、私はこの習慣に反対することを勧めます。

まず、クラスへのインターフェイスがどのように進化するかがわからず、他のプログラマが悪いことをするのを防ぎたいと考えています。参照によってshared_ptrを渡すことは、慣用的ではないため、プログラマーが見ることを期待するものではなく、誤って使用しやすくなります。防御的にプログラムする:インターフェースを誤って使いにくくします。参照渡しは、後で問題を引き起こすだけです。

次に、この特定のクラスが問題になることがわかるまで、最適化しないでください。最初にプロファイルを作成し、次に、プログラムが参照渡しによるブーストを本当に必要とする場合は、多分そうです。それ以外の場合は、設計、データ構造、アルゴリズム、および長期的な保守性について心配する代わりに、小さなもの(つまり、値で渡すために必要な追加のN命令)を気にしないでください。


litbの答えは技術的に正しいですが、プログラマーの「怠惰」を過小評価しないでください(私も怠惰です!)。littlenagの答えは、shared_ptrへの参照が予期せず、おそらく(おそらく)不必要な最適化によって将来のメンテナンスがより困難になるということです。
netjeff 2008年

18

はい、リファレンスをとっても問題ありません。メソッドに所有権を共有させるつもりはありません。それを使いたいだけです。とにかくそれをコピーするので、最初のケースの参照も取ることができます。しかし、最初のケースでは、所有権が必要です。まだ一度だけコピーするためのこのトリックがあります:

void ClassA::take_copy_of_sp(boost::shared_ptr<foo> sp) {
    m_sp_member.swap(sp);
}

また、それを返すときにコピーする必要があります(つまり、参照を返さないでください)。あなたのクラスはクライアントがそれを使って何をしているのかを知らないからです(それへのポインタを格納するとビッグバンが起こります)。後でそれがボトルネック(最初のプロファイル!)であることが判明した場合でも、参照を返すことができます。


編集:もちろん、他の人が指摘するように、これは、コードを知っていて、渡された共有ポインターを何らかの方法でリセットしないことがわかっている場合にのみ当てはまります。疑わしい場合は、値で渡します。


11

shared_ptrs を渡すことは賢明const&です。これは問題を引き起こしそうになく(shared_ptrEarwickerによって詳述されているように、参照が関数呼び出し中に削除されるというまれなケースを除いて)、これらをたくさん渡すとより速くなる可能性があります。覚えておいてください。デフォルトboost::shared_ptrはスレッドセーフであるため、コピーするとスレッドセーフの増分が含まれます。

一時オブジェクトは非const参照によって渡されない可能性があるためconst&、だけ&ではなくを使用してください。(MSVCの言語拡張により、とにかくそれを行うことができます)


3
はい、私は常にconst参照を使用しています。例に入れるのを忘れていました。とにかく、MSVCは非const参照をバグではなく一時ファイルにバインドすることを許可しますが、デフォルトではプロパティ「C / C ++->言語->言語の無効化」が「NO」に設定されているためです。それを有効にすると、コンパイルされません...
abigagli

abigagli:マジ?甘い!私はこれを職場で、明日最初に強制します;)
Magnus Hoff

10

2番目のケースでは、これを行う方が簡単です。

Class::only_work_with_sp(foo &sp)
{    
    ...  
    sp.do_something();  
    ...  
}

あなたはそれを次のように呼ぶことができます

only_work_with_sp(*sp);

3
ポインターのコピーを取得する必要がないときにオブジェクト参照を使用する規則を採用すると、意図も文書化するのに役立ちます。また、const参照を使用する機会も与えられます。
Mark Ransom、

はい、私は、呼び出された関数がそのオブジェクトについて何も「覚えていない」ことを表現する手段としてのオブジェクトへの参照の使用に同意します。通常、関数がオブジェクトの "追跡"である場合は、ポインターの仮引数を使用します
abigagli

3

関数が明示的にポインターを変更する場合を除き、「プレーン」な参照は避けます。

const &小さな関数を呼び出すとき、A は賢明なマイクロ最適化であるかもしれません-例えば、いくつかの条件をインライン化するようなさらなる最適化を可能にするため。また、増分/減分は(スレッドセーフであるため)同期ポイントです。ただし、これがほとんどのシナリオで大きな違いをもたらすとは思わないでしょう。

一般的に、特に理由がない限り、よりシンプルなスタイルを使用する必要があります。次に、const &一貫して使用するか、数か所だけで使用する場合はなぜかについてコメントを追加します。


3

私はconst参照によって共有ポインターを渡すことを提唱します-ポインターと共に渡される関数はポインターを所有しないというセマンティクスは、開発者にとってクリーンなイディオムです。

唯一の落とし穴は、複数のスレッドプログラムで、共有ポインターによってポイントされているオブジェクトが別のスレッドで破棄されることです。したがって、共有スレッドのconst参照の使用は、シングルスレッドプログラムでは安全であると言えます。

非const参照で共有ポインタを渡すと危険な場合があります。理由は、関数が戻った後も有効と見なされるオブジェクトを破棄するために関数が内部で呼び出すスワップ関数とリセット関数です。

時期尚早な最適化についてではなく、何をしたいのかが明確で、他の開発者によってコーディングイディオムがしっかりと採用されている場合は、CPUサイクルの不要な無駄を回避するためだと思います。

ちょうど私の2セント:-)


1
デビッド・シュワルツによる上記のコメントを参照してください "...私は、共有ポインターへの参照を渡すことによる、非常に深刻なバグを追いかけました。コードはオブジェクトの状態変化を処理していて、オブジェクトの状態が変化したことに気付いたとき、前の状態のオブジェクトのコレクションから削除し、新しい状態のオブジェクトのコレクションに移動しました。削除操作により、オブジェクトへの最後の共有ポインターが破棄されました。共有ポインターへの参照でメンバー関数が呼び出されましたコレクションで。ブーム...」
ジェイソンハリソン

3

ここでの長所と短所はすべて、実際にはshared_ptrだけでなく、参照によって渡されるANY型に一般化できるようです。私の意見では、参照渡し、const参照、および値渡しのセマンティクスを理解し、正しく使用する必要があります。しかし、すべての参照が悪いと思わない限り、shared_ptrを参照で渡すことには本質的に問題はありません...

例に戻るには:

Class::only_work_with_sp( foo &sp ) //Again, no copy here  
{    
    ...  
    sp.do_something();  
    ...  
}

sp.do_something()ぶら下がりポインタが原因で爆発しないことをどのようにして知っていますか?

真実は、shared_ptrであるかどうか、constであるかどうかに関係なく、spスレッド間で所有権を直接または間接的に共有したり、オブジェクトを誤用しdelete thisたり、循環所有権やその他の所有権エラーが発生するなどの設計上の欠陥がある場合に発生する可能性があります。


2

私がまだ言及していないことの1つは、参照によって共有ポインターを渡すときに、派生クラス共有ポインターを基本クラス共有ポインターへの参照を通じて渡したい場合に得られる暗黙の変換を失うことです。

たとえば、このコードはエラーを生成しますがtest()、共有ポインタが参照によって渡されないように変更した場合は機能します。

#include <boost/shared_ptr.hpp>

class Base { };
class Derived: public Base { };

// ONLY instances of Base can be passed by reference.  If you have a shared_ptr
// to a derived type, you have to cast it manually.  If you remove the reference
// and pass the shared_ptr by value, then the cast is implicit so you don't have
// to worry about it.
void test(boost::shared_ptr<Base>& b)
{
    return;
}

int main(void)
{
    boost::shared_ptr<Derived> d(new Derived);
    test(d);

    // If you want the above call to work with references, you will have to manually cast
    // pointers like this, EVERY time you call the function.  Since you are creating a new
    // shared pointer, you lose the benefit of passing by reference.
    boost::shared_ptr<Base> b = boost::dynamic_pointer_cast<Base>(d);
    test(b);

    return 0;
}

1

私はあなたが時期尚早な最適化に精通していて、学術的な目的のために、またはパフォーマンスの低い既存のコードを分離したためにこれを求めていると想定します。

参照渡しは問題ありません

const参照による受け渡しの方が優れており、指し示すオブジェクトにconst-nessを強制しないため、通常は使用できます。

参照を使用するためにポインタを失うリスクはありません。その参照は、スタックの初期にスマートポインタのコピーがあり、1つのスレッドだけが呼び出しスタックを所有しているため、既存のコピーが失われないことを示しています。

参照を使用することで、多くの場合、あなたが言及の理由で、より効率的な、しかし、保証の限りではありません。オブジェクトの逆参照も作業が必要になる場合があることに注意してください。理想的な参照使用シナリオは、コーディングスタイルに多数の小さな関数が含まれる場合で、ポインターは関数から関数へ渡されてから使用されます。

スマートポインターを参照として保存することは常に避けてください。あなたのClass::take_copy_of_sp(&sp)例はその正しい使い方を示しています。


1
「参照を使用したためにポインターを失うリスクはありません。その参照は、スタックの初期にスマートポインターのコピーがあることの証拠です」またはデータメンバー...?
Daniel Earwicker、2008年

boost :: threadとboost :: ref:boost :: function <int> functionPointer = boost :: bind(doSomething、boost :: ref(sharedPtrInstance));の魔法を考える。m_workerThread = new boost :: thread(functionPointer); ... sharedPtrInstanceを削除する
Jason Harrison

1

constの正確性(または、渡されるデータの所有権を関数が変更または共有できるようにすることを意味する)を想定していない場合、値によってboost :: shared_ptrを渡す方が、参照として渡すよりも安全です。オリジナルのboost :: shared_ptrが自身のライフタイムを制御することを許可します。次のコードの結果を検討してください...

void FooTakesReference( boost::shared_ptr< int > & ptr )
{
    ptr.reset(); // We reset, and so does sharedA, memory is deleted.
}

void FooTakesValue( boost::shared_ptr< int > ptr )
{
    ptr.reset(); // Our temporary is reset, however sharedB hasn't.
}

void main()
{
    boost::shared_ptr< int > sharedA( new int( 13 ) );
    boost::shared_ptr< int > sharedB( new int( 14 ) );

    FooTakesReference( sharedA );

    FooTakesValue( sharedB );
}

上記の例から、sharedAを参照で渡すと、FooTakesReferenceが元のポインターをリセットできるようになるため、使用カウントが0に減り、データが破壊されます。ただし、FooTakesValueは元のポインタをリセットできず、sharedBのデータが引き続き使用可能であることを保証します。別の開発者が必然的にやって来て、sharedAの脆弱な存在に便乗しようとすると、混乱が起こります。しかし、幸運なsharedB開発者は、すべてが彼の世界で正しいので、早く帰宅します。

この場合、コードの安全性は、コピーによって作成される速度向上をはるかに上回ります。同時に、boost :: shared_ptrはコードの安全性を向上させることを目的としています。何かがこの種のニッチな最適化を必要とする場合、コピーから参照に移動する方がはるかに簡単です。


1

サンディは書いている:「ここですべての長所と短所は実際にshared_ptrだけでなく、参照によって渡されるすべての型に一般化できるようです。」

ある程度正しいですが、shared_ptrを使用する目的は、オブジェクトの存続期間に関する懸念を排除し、コンパイラーにそれを処理させることです。参照によって共有ポインターを渡し、参照カウント対象オブジェクトのクライアントがオブジェクトデータを解放する可能性のある非constメソッドを呼び出せるようにする場合、共有ポインターを使用してもほとんど意味がありません。

パフォーマンスが問題になる可能性があり、まれに正当化される可能性があるため、前の文で「ほぼ」と書きましたが、このシナリオを自分で避け、真剣に調べるなど、他のすべての可能な最適化ソリューションを自分で探します別のレベルの間接参照、遅延評価などを追加するとき。

作成者の過去に存在するコード、または作成者のメモリに投稿するコードでさえ、動作、特にオブジェクトの有効期間に関する動作について暗黙の前提が必要であり、明確で簡潔で読みやすいドキュメントが必要であり、多くのクライアントはそれをとにかく読まないでしょう。単純さはほとんどの場合効率よりも優先され、効率を上げる方法は他にもほとんど常にあります。参照によってカウントされるオブジェクト(および等号演算子)のコピーコンストラクターによるディープコピーを回避するために、参照によって値を渡す必要がある場合は、ディープコピーされたデータを参照カウントポインターにする方法を検討する必要があります。すばやくコピーしました。(もちろん、それはあなたの状況に当てはまらない可能性がある1つの設計シナリオにすぎません)。


1

私は以前、スマートポインターを値で渡すという原則が非常に強力であるプロジェクトで働いていました。パフォーマンス分析を依頼されたとき-スマートポインターの参照カウンターのインクリメントとデクリメントのために、アプリケーションが使用プロセッサー時間の4〜6%を費やしていることがわかりました。

Daniel Earwickerから説明されているように、奇妙なケースで問題が発生するのを避けるためにスマートポインターを値で渡したい場合は、それに対して支払う価格を理解してください。

参照を使用することにした場合、const参照を使用する主な理由は、インターフェイスで使用するクラスを継承するクラスからオブジェクトに共有ポインターを渡す必要がある場合に暗黙的なアップキャストを可能にすることです。


0

litbが言ったことに加えて、おそらく2番目の例ではconst参照によって渡されることになるので、誤って変更しないようにしてください。


0
struct A {
  shared_ptr<Message> msg;
  shared_ptr<Message> * ptr_msg;
}
  1. 値渡し:

    void set(shared_ptr<Message> msg) {
      this->msg = msg; /// create a new shared_ptr, reference count will be added;
    } /// out of method, new created shared_ptr will be deleted, of course, reference count also be reduced;
  2. 参照渡し:

    void set(shared_ptr<Message>& msg) {
     this->msg = msg; /// reference count will be added, because reference is just an alias.
     }
  3. ポインタ渡し:

    void set(shared_ptr<Message>* msg) {
      this->ptr_msg = msg; /// reference count will not be added;
    }

0

すべてのコードは、ある程度の意味を持っている必要があります。アプリケーションのあらゆる場所で共有ポインタを値渡しする場合、これは「他の場所で何が行われているのかわからないため、生の安全性を支持する」ことを意味します。これは、コードを調べることができる他のプログラマーにとって、私が信頼のサインと呼ぶものではありません。

とにかく、関数がconst参照を取得していて「不明」な場合でも、関数の先頭に共有ポインターのコピーを作成して、ポインターへの強い参照を追加できます。これは、デザインに関するヒントと見なすこともできます(「ポインターは他の場所で変更できます」)。

だから、はい、IMO、デフォルトは「const参照で渡す」である必要があります。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.