最終手段としてのstd :: shared_ptr?


59

「Going Native 2012」ストリームを見ているだけで、についての議論に気付きましたstd::shared_ptr。Bjarneのやや否定的な見方std::shared_ptrと、オブジェクトの寿命が不確かな場合の「最後の手段」として使用すべきだという彼のコメントを聞いて、私は少し驚いていました(彼によると、そうではないはずです)。

これをもう少し詳しく説明したい人はいますか?どうすればstd::shared_ptrオブジェクトの寿命を安全な方法で管理し、管理できますか?


8
ポインターを使用していませんか?オブジェクトの別個の所有者がいて、それがライフタイムを管理していますか?
ボーパーソン

2
明示的に共有されたデータはどうですか?ポインターを使用しないのは難しいです。また、その場合、std :: shared_pointerはダーティな「ライフタイムの管理」を行います
カミルクリメック

6
提示されたアドバイスに耳を傾けず、そのアドバイスの背後にある議論に耳を傾けることを検討しましたか?彼は、この種のアドバイスが機能するシステムの種類を非常によく説明しています。
ニコルボーラス

@NicolBolas:私はアドバイスと議論を聞いたが、明らかに十分に理解しているとは感じなかった。
-ronag

彼は何時に「最後の手段」と言いますか?(channel9.msdn.com/Events/GoingNative/GoingNative-2012/…)の36分でビットを見て、彼はポインターの使用には警戒していると言いますが、shared_ptrとunique_ptrだけでなく、 '通常のポインター。彼は、オブジェクト自体(newで割り当てられたオブジェクトへのポインターではない)を優先すべきだと示唆しています。プレゼンテーションの後半で考えていた部分はありましたか?
ファラプ14

回答:


55

所有権の共有を回避できる場合、アプリケーションはよりシンプルでわかりやすくなり、メンテナンス中に発生するバグの影響を受けにくくなります。所有権モデルが複雑または不明確であると、簡単に追跡できない共有状態を介して、アプリケーションのさまざまな部分の結合を追跡するのが困難になる傾向があります。

これを考えると、自動ストレージ期間を持つオブジェクトを使用し、「値」サブオブジェクトを持つことが望ましいです。これに失敗unique_ptrすると、shared_ptr最後の手段ではないにしても、望ましいツールのリストの下にあるという点で、良い選択肢になるかもしれません。


5
+1は、問題がテクノ自体(共有所有権)ではなく、何が起こっているのかを解読しなければならない単なる人間であるために私たちに導入する難しさであることを指摘したためです。
マチューM.

ただし、このようなアプローチを採用すると、ほとんどの非自明なOOPクラスに並行性プログラミングパターンを適用するプログラマーの能力が大幅に制限されます(コピー不可のため)。この問題は「Going Native 2013」で発生します。
rwong

48

Bjarneが住んでいる世界は非常に...学問的であり、より良い用語を求めています。オブジェクトが非常に意図的な関係階層を持つようにコードを設計および構造化できる場合、所有権の関係が厳格で不屈になり、コードは一方向(高レベルから低レベルへ)に流れ、オブジェクトは下位のオブジェクトとのみ通信します階層の場合、の必要性はあまりありませんshared_ptr。誰かがルールを破らなければならないようなまれな機会に使用するものです。しかし、それ以外の場合は、vector値セマンティクスを使用するsまたはその他のデータ構造、およびunique_ptrsを単独で割り当てる必要のあるものにsをすべて貼り付けることができます。

それは住むのに素晴らしい世界ですが、それはあなたが常にやることを得るものではありません。そのようにコードを整理できない場合、作成しようとしているシステムの設計が不可能である(または非常に不快な)ことを意味するため、オブジェクトの共有所有権がますます必要になります。

このようなシステムでは、裸のポインタを保持することは...正確には危険ではありませんが、疑問が生じます。すばらしいことは、オブジェクトの寿命について合理構文上の保証をshared_ptr提供することです。壊れますか?もちろん。しかし、人々も物事をすることができます。の基本的なケアと給餌は、所有権を共有しなければならない割り当てられたオブジェクトに適切な生活の質を提供する必要があります。const_castshared_ptr

次に、がありますweak_ptr。これは、がない場合は使用できませんshared_ptr。システムが厳密に構造化されている場合は、アプリケーションの構造により、指定されたオブジェクトが確実に存続することを保証して、あるオブジェクトへのネイキッドポインターを保存できます。内部値または外部値へのポインターを返す関数を呼び出すことができます(たとえば、Xという名前のオブジェクトを検索します)。適切に構造化されたコードでは、オブジェクトの寿命が自分の寿命を超えることが保証されている場合にのみ、その関数を使用できます。したがって、そのネイキッドポインターをオブジェクトに格納することは問題ありません。

実際のシステムではその剛性を常に達成できるとは限らないため、寿命を合理的に確保する何らかの方法が必要です。場合によっては、完全な所有権は必要ありません。時々、ポインターが悪いか良いかを知る必要があるだけです。どこからだweak_ptrにしています。私は例があった可能性が使用されてきたunique_ptrかをboost::scoped_ptr、私は使用していたshared_ptr私はので、具体的に誰かに「揮発性」のポインタを与える必要がありました。寿命のあるポインターは不確定であり、ポインターが破壊されたときに照会できました。

世界の状態が不確定なときに生き残るための安全な方法。

viaの代わりに、ポインターを取得する関数呼び出しによってそれを行うことができましたweak_ptrか?はい、しかしそれはもっと簡単に壊れる可能性があります。ネイキッドポインターを返す関数には、ユーザーがそのポインターを長期間保存するようなことをしないことを構文的に示唆する方法がありません。shared_ptrまた、aを返すと、誰かが単純にそれを保管し、オブジェクトの寿命を延ばす可能性が非常に高くなります。weak_ptrしかし、を返すことは、shared_ptr取得したものを保存することlockは...疑わしいアイデアであることを強く示唆しています。それをやめることはありませんが、C ++ではコードを壊すことを妨げるものはありません。weak_ptr自然なことをすることからの最小の抵抗を提供します。

さて、それは使いすぎshared_ptrないということではありません。確かにできます。特にpre- では、RAIIポインターを渡したりリストに入れたりする必要があるため、aを使用した場合が多くありました。ムーブセマンティクスがなければ、唯一の真の解決策でした。unique_ptrboost::shared_ptrunique_ptrboost::shared_ptr

そして、あなたはそれが全く不必要な場所でそれを使うことができます。上記のように、適切なコード構造により、の一部の使用の必要性を排除できshared_ptrます。しかし、システムをそのように構成できず、それでも必要なことを実行できれば、shared_ptr非常に役立ちます。


4
+1:例えばboost :: asioを見てください。アイデアは多くの分野に及ぶと思いますが、コンパイル時にオブジェクトを放棄する最後のUIウィジェットまたは非同期呼び出しがわからない場合があり、shared_ptrを使用する必要はありません。これは明らかにすべての状況に適用されるわけではなく、ツールボックス内の別の(非常に便利な)ツールです。
ガイサートン

3
コメントが少し遅れました。shared_ptrc ++がPythonなどのスクリプト言語と統合されているシステムに最適です。を使用boost::pythonすると、C ++およびPython側での参照カウントが大幅に協力します。C ++のオブジェクトはすべてPythonで保持でき、死ぬことはありません。
eudoxos

1
参考までに、私の理解はWebKitもChromiumの使用でもありませんshared_ptr。両方ともの独自の実装を使用しintrusive_ptrます。両方ともC ++で書かれた大規模なアプリケーションの実世界の例であるため、私はそれを取り上げます
-gman

1
@gman:Stroustrupの異議shared_ptrは等しく当てはまるので、あなたのコメントは非常に誤解を招くと思いますintrusive_ptr。彼は、コンセプトの特定のスペルではなく、共有所有権のコンセプト全体に異議を唱えています。だから、この質問の目的のために、それらは、大規模なアプリケーションの二つの実世界での例です使用しますshared_ptr。(さらに、shared_ptr有効になっていない場合でも有用であることを示していweak_ptrます。)
ruakh

1
FWIW、Bjarneは学術界に住んでいるという主張に対抗するために:私の純粋な産業経歴(G20証券取引所の共同設計と500KプレーヤーMOGの設計のみを含む)の中で、本当に必要なのは3件だけでした共有所有権。私はここでBjarneと200%です。
いいえバグうさぎ

37

使ったことがないと思うstd::shared_ptr

ほとんどの場合、オブジェクトはコレクションに関連付けられ、そのコレクションはライフタイム全体にわたって属します。その場合は、whatever_collection<o_type>またはを使用するだけでwhatever_collection<std::unique_ptr<o_type>>、そのコレクションはオブジェクトまたは自動変数のメンバーになります。もちろん、動的な数のオブジェクトが必要ない場合は、固定サイズの自動配列を使用できます。

コレクションの繰り返しやオブジェクトに対する他の操作は、所有権を共有するためのヘルパー関数を必要としません... オブジェクトを使用してから戻り、呼び出し側はオブジェクトが呼び出し全体にわたって生き続けることを保証します。これは、発信者と着信者の間で最もよく使用される契約です。


ニコルボーラスは、「あるオブジェクトが裸のポインタを保持し、そのオブジェクトが死んだら...おっと」とコメントしました。「オブジェクトは、オブジェクトがそのオブジェクトの存続期間を確実に通過することを保証する必要があります。shared_ptrそれを行うことができるだけです。」

私はその議論を買いません。少なくとも、shared_ptrこの問題は解決しません。どうですか:

  • 何らかのハッシュテーブルがオブジェクトを保持し、そのオブジェクトのハッシュコードが変更された場合...おっと。
  • ある関数がベクトルを反復していて、そのベクトルに要素が挿入されている場合...おっと。

ガベージコレクションと同様に、デフォルトの使用によりshared_ptr、プログラマはオブジェクト間、または関数と呼び出し元間のコントラクトについて考えないようになります。正しい前提条件と事後条件について考えることが必要であり、オブジェクトの寿命はその大きなパイのほんの一部です。

オブジェクトは「死ぬ」ことはなく、コードの一部がそれらを破壊します。そしてshared_ptr、通話契約を理解する代わりに問題を投げることは、誤った安全性です。


17
@ronag:「生のポインターは悪い」ので、生のポインターがより良い場所で使用し始めたのではないかと思います。しかし、生のポインタは悪くありません。オブジェクトへの最初の所有ポインタのみを生のポインタにするのは悪いことです。それは、例外が存在する場合に、メモリを手動で管理する必要があるためです。しかし、生のポインタをハンドルまたはイテレータとして使用するだけで十分です。
ベンフォークト

4
@BenVoigt:もちろん、裸のポインタを渡すことの難しさは、オブジェクトの寿命を知らないことです。あるオブジェクトが裸のポインタを保持していて、そのオブジェクトが死んだら...おっと。それはまさにそのようなことでshared_ptrありweak_ptr、避けるように設計されました。Bjarneは、すべてが素晴らしく明示的な寿命を持ち、すべてがその周りに構築されている世界に住もうとしています。そして、もしあなたがその世界を構築できるなら、素晴らしい。しかし、それは現実の世界ではそうではありません。オブジェクトは、そのオブジェクトがそのオブジェクトの存続期間中存続することを保証する必要があります。shared_ptrできるのはそれだけです。
ニコルボーラス

5
@NicolBolas:それは誤った安全性です。関数の呼び出し元が通常の保証を提供していない場合:「このオブジェクトは関数呼び出し中に外部の当事者によって触れられない」場合、両方のタイプの外部変更が許可されることに同意する必要があります。 shared_ptr特定の外部変更を1つだけ緩和し、最も一般的な変更も緩和しません。また、関数呼び出し規約で別の方法で指定されている場合、オブジェクトの寿命が正しいことを保証するのはオブジェクトの責任ではありません。
ベンフォークト

6
@NicolBolas:関数がオブジェクトを作成し、ポインターで返す場合unique_ptr、オブジェクトへのポインターが1つだけ存在し、所有権を持っていることを表すaでなければなりません。
ベンフォークト

6
@Nicol:あるコレクションでポインターを検索する場合、そのコレクションにあるポインター型を使用するか、コレクションが値を保持している場合は生のポインターを使用する必要があります。オブジェクトを作成しているときに、呼び出し元がa shared_ptrを必要とする場合でも、aを返しますunique_ptr。からunique_ptrへの変換shared_ptrは簡単ですが、その逆は論理的に不可能です。
ベンフォークト

16

私は絶対的な用語(「最後の手段」など)で考えるのではなく、問題の領域に関連するものを好みます。

C ++は、ライフタイムを管理するためのさまざまな方法を提供できます。それらのいくつかは、スタック駆動型の方法でオブジェクトを再構築しようとします。他のいくつかは、この制限を回避しようとします。それらのいくつかは「リテラル」であり、他のいくつかは近似です。

実際にできること:

  1. 純粋な値のセマンティクスを使用します。重要なことは、比較的小さなオブジェクトのための作品は、2つのことを仮定することができ、「値」およびない「アイデンティティ」、されてPersonそれを有するがname同じ人物(:同じの2つの表現より良い)。寿命はマシンスタックによって付与され、プログラムの終了は本質的に重要ではありません(はそれが何であるかに関係なく、名前であるPersonため)
  2. スタックに割り当てられたオブジェクト、および関連する参照またはポインターを使用します。ポリモーフィズムを許可し、オブジェクトの有効期間を許可します。「スマートポインター」は必要ありません。スタック内にあるオブジェクトが指すオブジェクトよりも長い間、オブジェクトを「指す」ことができないためです(最初にオブジェクトを作成し、次にそれを参照する構造体)。
  3. スタック管理ヒープ割り当てオブジェクトを使用します。これはstd :: vectorとすべてのコンテナが行うことであり、wat std::unique_ptrはそれを行います(サイズ1のベクトルと考えることができます)。繰り返しになりますが、オブジェクトが参照するデータ構造の前(後)にオブジェクトが存在し始める(そして存在が終わる)ことを認めます。

この方法の弱点は、オブジェクトの種類と量が、作成される場所に関してより深いスタックレベルの呼び出しの実行中に変化できないことです。この手法はすべて、オブジェクトの作成と削除がユーザーアクティビティの結果であるすべての状況で強度を「失敗」させるため、オブジェクトのランタイムタイプはコンパイル時に認識されず、オブジェクトを参照する構造が過剰になる可能性があります。ユーザーは、より深いスタックレベルの関数呼び出しから削除するように求めています。この場合、次のいずれかを行う必要があります。

  • オブジェクトおよび関連する参照構造の管理に関する規律を導入するか...
  • どういうわけか「純粋なスタックベースのライフタイムを逃れる」というダークサイドに行きましょう。オブジェクトは、それらを作成した関数とは無関係に離れなければなりません。そして、彼らが必要になるまで ...去らなければなりません。

C ++ isteslfにはwhile(are_they_needed)、そのイベントを監視するネイティブメカニズムがありません()。したがって、次のように近似する必要があります。

  1. 共有所有権を使用します。オブジェクトの寿命は「参照カウンタ」にバインドされます。「所有権」を階層的に編成できる場合に機能し、所有権ループが存在する可能性がある場所で失敗します。これはstd :: shared_ptrが行うことです。そして、weak_ptrを使用してループを解除できます。これはほとんどの場合に機能しますが、多くのデザイナーが異なるチームで働いており、誰がかび臭いものを所有しているのかについて明確な理由(何かが二重に好きなチェーンである場合)はありません。以前のおかげで、次は前を参照するか、次は前を所有するか、次を所有するか?要件を満たさない場合、ソリューションは同等であり、大規模プロジェクトではそれらを混同するリスクがあります)
  2. ガベージコレクションヒープを使用する:ライフタイムを気にしないでください。コレクターを時々実行すると、到達不能であるものは「もう必要ない」と見なされ、...まあ...ああ...破壊されましたか?確定しましたか?フローズン?。GCコレクターは多数ありますが、実際にC ++に対応しているものは見つかりません。それらのほとんどは、オブジェクトの破壊を気にせずにメモリを解放します。
  3. 適切な標準メソッドインターフェイスを備えたC ++対応のガベージコレクターを使用します。それを見つけるために頑張ってください。

最後の解決策の最初の解決策に進むと、オブジェクトのライフタイムを管理するために必要な補助データ構造の量が増加し、オブジェクトの整理と保守に時間がかかるようになります。

ガベージコレクターにはコストがあり、shared_ptrにはさらに少なく、unique_ptrにはさらに少なく、スタック管理オブジェクトにはほとんどありません。

であるshared_ptr「最後の手段」?。いいえ、そうではありません。最後の手段はガベージコレクターです。 shared_ptr実際にstd::提案されている最後の手段です。しかし、あなたが私が説明した状況にいるなら、適切な解決策かもしれません。


9

後のセッションでハーブサッターが言及したことの1つは、をコピーするたびにshared_ptr<>、連動する増分/減分が発生するということです。マルチコアシステム上のマルチスレッドコードでは、メモリの同期は重要ではありません。選択肢があれば、スタック値を使用するか、またはunique_ptr<>参照または生のポインターを渡すことをお勧めします。


1
またはパスshared_ptr...左辺値または右辺値参照で
ronag

8
重要shared_ptrなのは、標準にあるからといってメモリリークの問題をすべて解決するのは、特効薬のように使用しないことです。これは魅力的なトラップですが、リソースの所有権を認識することは依然として重要であり、その所有権が共有されない限り、a shared_ptr<>は最良の選択肢ではありません。
Eclipseの

私にとって、これは最も重要でない詳細です。時期尚早の最適化を参照してください。ほとんどの場合、これは決定を下すものではありません。
ガイサートン

1
@gbjbaanb:はい、それらはCPUレベルですが、マルチコアシステムでは、キャッシュを無効にし、メモリバリアを強制します。
Eclipse

4
私が取り組んだゲームプロジェクトでは、パフォーマンスの違いが非常に大きく、2つの異なるタイプの参照カウントポインターが必要になることがわかりました。1つはスレッドセーフで、もう1つはそうではありませんでした。
キロタン

7

最後の「リゾート」が彼が使用した正確な単語であったかどうかは覚えていませんが、彼が言ったことの実際の意味は最後の「選択」であったと信じています。unique_ptr、weak_ptr、shared_ptr、さらにはネイキッドポインターにも場所があります。

彼らが同意したことの1つは、私たち(開発者、本の著者など)がすべてC ++ 11の「学習段階」にあり、パターンとスタイルが定義されているということです。

例として、Herbは、Cでの業界の経験とベストプラクティスから数年後に、Effective C ++(Meyers)やC ++ Coding Standards(Sutter&Alexandrescu)などのいくつかの重要なC ++ブックの新しいエディションを期待すべきだと説明しました。 ++ 11はパンアウトします。


5

彼が得ているのは、標準的なポインタ(グローバルな置換のようなもの)を書いたときはいつでもshared_ptrを書くことが誰にでも一般的になり、実際に設計するのではなく、少なくともオブジェクトの作成と削除の計画。

人々が忘れているもう1つのことは(上記の資料で述べたロック/更新/ロック解除のボトルネック以外に)、shared_ptrだけではサイクルの問題を解決できないということです。shared_ptrでリソースをリークする可能性はあります:

オブジェクトAは、別のオブジェクトAへの共有ポインターを含みます。オブジェクトBは、A a1とA a2を作成し、a1.otherA = a2を割り当てます。およびa2.otherA = a1; これで、a1、a2の作成に使用したオブジェクトBの共有ポインターが範囲外になります(関数の最後など)。これでリークが発生しました。他の誰もa1とa2を参照していませんが、互いを参照しているため、参照カウントは常に1であり、リークしています。

これは簡単な例です。実際のコードでこれが発生すると、通常は複雑な方法で発生します。weak_ptrには解決策がありますが、非常に多くの人がどこでもshared_ptrを実行するだけで、リークの問題やweak_ptrのことすら知りません。

まとめると、OPが参照するコメントは次のように要約されると思います。

使用している言語(管理対象、管理対象外、またはshared_ptrなどの参照カウントの中間)に関係なく、オブジェクトの作成、存続期間、および破棄を理解し、意図的に決定する必要があります。

編集:それが「不明、shared_ptrを使用する必要がある」という意味であっても、あなたはそれをまだ考えており、意図的にそうしています。


3

すべてのオブジェクトが参照カウントされてヒープに割り当てられる言語であるObjective-Cの経験からお答えします。オブジェクトを処理する方法が1つあるため、プログラマにとって物事はずっと簡単です。これにより、標準の規則を定義することができます。この規則に従うと、コードの堅牢性とメモリリークが保証されます。また、最新のARC(自動参照カウント)のように、巧妙なコンパイラー最適化が可能になりました。

私のポイントは、shared_ptrが最後の手段ではなく最初の選択肢であるべきだということです。既定で参照カウントを使用するか、他のオプションを使用するのは、実行していることが確実な場合のみにしてください。生産性が向上し、コードがより堅牢になります。


1

私は質問に答えようとします:

std :: shared_ptrを使用せずにプログラミングし、オブジェクトの有効期間を安全に管理するにはどうすればよいですか?

C ++には、メモリを実行するためのさまざまな方法が多数あります。次に例を示します。

  1. struct A { MyStruct s1,s2; };クラススコープでshared_ptrの代わりに使用します。これは、依存関係がどのように機能するかを理解する必要があり、依存関係をツリーに制限するのに十分な依存関係を制御する必要があるため、上級プログラマー専用です。ヘッダーファイル内のクラスの順序は、これの重要な側面です。この使用法は、ネイティブの組み込みc ++型では既に一般的ですが、プログラマ定義のクラスでの使用は、これらの依存関係とクラスの順序の問題のため、あまり使用されていないようです。このソリューションには、sizeofにも問題があります。プログラマーは、この問題を前方宣言または不必要な#includeを使用するための要件と見なしているため、多くのプログラマーはポインターの下位ソリューションに戻り、後でshared_ptrに戻ります。
  2. MyClass &find_obj(int i);代わりに+ clone()を使用しshared_ptr<MyClass> create_obj(int i);ます。多くのプログラマーは、新しいオブジェクトを作成するためのファクトリーを作成したいと考えています。shared_ptrは、この種の使用に最適です。問題は、単純なスタックまたはオブジェクトベースのソリューションではなく、ヒープ/フリーストア割り当てを使用した複雑なメモリ管理ソリューションを既に想定していることです。優れたC ++クラス階層は、1つだけでなく、すべてのメモリ管理スキームをサポートします。参照ベースのソリューションは、ローカル関数スコープ変数を使用する代わりに、返されたオブジェクトが包含オブジェクト内に格納されている場合に機能します。所有権を工場からユーザーコードに渡すことは避けてください。find_obj()を使用した後のオブジェクトのコピーは、オブジェクトを処理するための良い方法です。通常のコピーコンストラクターおよび通常のコンストラクター(異なるクラス)のrefrerenceパラメーターまたは多相オブジェクトのclone()で処理できます。
  3. ポインターまたはshared_ptrsの代わりに参照を使用します。すべてのc ++クラスにはコンストラクターがあり、各参照データメンバーを初期化する必要があります。この使用法により、ポインターとshared_ptrsの多くの使用を回避できます。メモリーがオブジェクトの内部にあるか外部にあるかを選択し、決定に基づいて構造体ソリューションまたは参照ソリューションを選択するだけです。通常、このソリューションの問題は、一般的ではあるが問題のあるプラクティスであるコンストラクターパラメーターの回避と、クラスのインターフェイスの設計方法の誤解に関連しています。

「所有権を工場からユーザーコードに渡すことは避けてください。」そして、それが不可能な場合はどうなりますか?「ポインタまたはshared_ptrsの代わりに参照を使用します。」いいえ。ポインターを再配置できます。参照はできません。これにより、クラスに格納されるものに建設時の制限が強制されます。それは多くのことにとって実用的ではありません。あなたの解決策は、より流動的なインターフェースと使用パターンのニーズに非常に硬く、柔軟性がないようです。
ニコルボーラス

@Nicol Bolas:上記の規則に従うと、refはオブジェクト間の依存関係に使用され、提案されたデータストレージには使用されません。依存関係はデータよりも安定しているため、お客様が検討していた問題に取り組むことはありません。
tp1

これは非常に簡単な例です。オブジェクトであるゲームエンティティがあります。別のオブジェクトを参照する必要があります。これは、通信する必要があるターゲットエンティティです。ただし、ターゲットは変更できます。ターゲットはさまざまなポイントで死ぬ可能性があります。そして、エンティティはこれらの状況に対処できる必要があります。厳格なノーポインターアプローチは、ターゲットが死ぬことは言うまでもなく、ターゲットを変更するような単純なことさえも処理できません。
ニコルボーラス

@nicol bolas:ああ、それは異なって扱われます。クラスのインターフェースは複数の「エンティティ」をサポートします。オブジェクトとエンティティ間の1:1マッピングの代わりに、entityarrayを使用します。エンティティは、配列から削除するだけで非常に簡単に消滅します。ゲーム全体に存在するエンティティ配列はごくわずかであり、配列間の依存関係はそれほど頻繁には変化しません:)
tp1

2
いいえ、unique_ptr工場に最適です。あなたは変えることができますunique_ptrshared_ptr、それは他の方向に行くことを論理的に不可能です。
ベンフォークト
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.