Scott Meyers による本書推薦の言葉 XI イディオムやデザイン・パターンの実装を生成するテンプレートの開発過程で , 全ての実装者が直面する設計 上の様々な意思決定に Andrei は取り組んでいます。コードはスレッド・セーフであるべきなのか ? スマート・ ポインタの参照外しに先だって , nu Ⅱ値のチェックを行うべきなのか ? プログラムの終了処理中で Singlet 。 n の破棄コードが , 既に破棄済みの他の Singleton を使用しようとした場合 , どうするべきなのか ? Andrei の目 標は , いずれかを指定するのではなく , 設計上で可能な選択を利用者に対して「全て」提供することなのです。 こういった意思決定をポリシー・クラスという形でカプセル化し , このポリシー・クラスを 彼の解決策とは , テンプレート・パラメータとして引き渡せるようにし , また適切なデフォルト値をクラスに対して設定しておく ことにより , 大半の利用者がそういったことを気にせず使用できるようにすることなのです。結果は驚くべきも のです。例えば , 本書で扱っているスマート・ポインタのテンプレートにはたった 4 つのパラメータしかありま せんが , これによってそれぞれが独自の動作を行う 300 種類 ( ! ) ものスマート・ポインタ型を生成できるので す。しかし , デフォルトのスマート・ポインタで満足できるプログラマは , ポリシー・パラメータを無視し , ス マート・ポインタが指すオプジェクトの型のみを指定するだけで , 実質的に何もしなくても , 巧妙に作り上げら れたスマート・ポインタ・クラスの利点を享受することができるのです。 最後に本書は , 3 つの技術的な話題を説得力ある方法で提供しています。まず第 1 は , 0 + におけるテンプ レートの強力さと柔軟性に対する新しい観点を提供している点です。 ( タイプリストの話題であなたがシャッポ を脱がなかったとしたら , それはあなたがシャッポそのものをかぶっていなかっただけなのでしよう。 ) 第 2 は , イディオムとパターンの実装の違いに横たわっている直交した次元を識別している点です。これはテンプレート 設計者やパターン実装者に対して重要な情報となるものですが , イディオムやパターンについて書かれたほとん こういった類の分析までは行われていないのです。第 3 は , Loki ( 本書で解説しているテンプ どの解説書では , レートのライプラリ名 ) のソースコードが公開され , 無料でダウンロードできるため , Andrei が考察しているイ ディオムとパターンに対応するテンプレートの実装を実際に試してみることができる点です。あなたが使ってい るコンパイラのテンプレート機能に対する負荷テストが行えるということはさておき , このソースコードをあな た自身が設計するテンプレートの素晴らしいスタート地点とすることができるはずです。もちろん , Andrei の コードを取り出して ( 合法的に ) 使用することもできます。彼自身も自らの努力を多くの人々に利用してもらい たいと考えていることでしよう。 私の見たところ , テンプレートを取り巻く状況は , 1995 年に私がそれについて記述することを避けた時から どんどん変わってきています。この速度で変革が続いていくとしたら , 私がテンプレートについて記述すること は , まずあり得ないでしよう。しかし幸いなことに , 私よりも勇ましい人達が沢山います。 Andrei はそういっ たパイオニアの 1 人なのです。彼の書籍から多くのことを学べるはずです。私も多くを学んだのですから。 Scott Meyers 2000 年 9 月
170 第 7 章スマート・ポインタ 該当オプジェクトを delete するというものです。また , その他の実装として , コピーを行った場合 , 指してい るオプジェクトが増幅されていくようなものもあります。 手短に言えば , スマート・ポインタの世界では , 所有権は重要な問題なのです。スマート・ポインタは , 所有 権の管理機能を提供することにより , 整合性の保証と , 値のセマンティックスを完全にサポートできるわけで す。スマート・ポインタの構築 , コピー , 破棄時には , 所有権に関する様々な作業を行わなければならないため , スマート・ポインタにはこういった機能が欠かせないわけです。 以下のセクションでは , スマート・ポインタの設計と実装について , 様々な観点から考察しています。その目 標は , スマート・ポインタを可能な限り生のポインタに近づける ( しかし近づけすぎないようにする ) ことにあ ります。これには , ある種の矛盾が含まれています。つまり , スマート・ポインタが馬鹿なポインタとまったく 同じように振る舞うのであれば , それは単に馬鹿なポインタでしかないのです。 スマート・ポインタと生のポインタの間を取り持つような互換性を実装する場合 , 正しく溝を埋める方法と破 滅に向かう方法との間には紙一重の差しかありません。追加するだけの価値があると思える機能であっても , そ れが利用者側に高い代償を要求する場合もあります。優れたスマート・ポインタを実装する秘訣は , こういった 機能集合のバランスをうまく取っていくことなのです。 7.3 スマート・ポインタの領域 まず手始めに , スマート・ポインタについての基本的な質問です。 p 。 intee ーの型は必ず T * なのでしようか ? そうでないのであれば , 他にどういった型が考えられるのでしよう ? ジェネリック・プログラミングでは , 常 ーういった質問を問いかけるべきなのです。ジェネリック・コード中に型をハードコードすると , コードの汎 用性が低下します。ジェネリック・コードにハードコードした型を記述することは , 通常のコードに定数を直接 切り込むことと同じなのです。 いくつかの状況では , ポイントされる型をカスタマイズできるようにしておく意味があります。例を挙げれ ば , 非標準のポインタ修飾子を取り扱うような場合です。 16 ビットのインテル 80X86 を使用していた時代 , --near, --far, —huge といった修飾子を用いてポインタの限定を行っていました。 別な状況として , スマート・ポインタを多重化する場合があります。誰かが実装したスマート・ポインタ LegacySmartptr く T> が手元にあり , これを拡張する必要が出てきたと考えて下さい。これはリスクを伴う決 定です。少しましな手段は , 自身でラッパとなるスマート・ポインタを作成し , その中に元のスマート・ポイ ンタを格納することでしよう。内側のスマート・ポインタはポインタ・シンタックスをサポートしているた め , こういったことが可能なはずです。外側のスマート・ポインタの観点に立てば , 指される型は T* ではなく LegacySmartPtr く T> ということになるわけです。 。 perat 。 r ー > のメカニズムを主に利用した , スマート・ポインタの多重化に関する興味深い応用事例がありま す。組み込みのポインタでない型に対して。 pe て at 。 r ー > を適用した場合 , コンパイラは面白いことを行います。 その型に対するユーザ定義の operator-> を検索 , 適用した後 , その結果に対してもう一度 operator →を適用 するのです。コンパイラは , こういったことを組み込み型へのポインタに到達するまで再帰的に行い , その後メ ンバのアクセスへと進むのです。このためスマート・ポインタの。 perator →は , ポインタを返す必要がないわ けです。つまり , 使用シンタックスを変えることなく , operator-> を実装した代理のオプジェクトを返すこと ができるのです。 これによって , 非常に面白いイディオムを考えつくことができます。事前および事後の関数呼び出しです
第 7 章 7.5 ュティープ・コピー方式 174 スマート・ポインタ ディープ・コピー方式でも , たいていの場合はポリモフィズムに則ったオプジェクトを扱うことになるため , のです。 ている動作や状態が正確に判っていないのに , そういったものをコピーしなければならないというのは面白いも ピーでは , ポリモフィズムに則った動作についてもコピーされて欲しいはずです。扱っている対象が実際に持っ き , 実際には導出されたクラスを指すようにした場合を考えてみましよう。こういったスマート・ポインタのコ トを安全に移動させるための乗り物なのです。スマート・ポインタに基底クラスへのポインタを保持させてお 答えはポリモフィズムをサポートするためです。スマート・ポインタは , ポリモフィズムに則ったオプジェク うか ? ジェクトを単純な値渡しで引き渡すだけで正しく動作する時に , スマート・ポインタを使用するのは何故でしょ タは通常の C+ + における値のセマンティックスに何も追加していないように見受けられます。指しているオプ 一目見ただけで , ディープ・コピー方式は動作が遅そうなことが判ります。そして , このスマート・ポイン 合の状態を描写したものです。 に delete することができます。図 7.1 は , ディープ・コピー方式によるスマート・ポインタを使用している場 タが 1 つだけになるわけです。従って , スマート・ポインタのデストラクタは , 指しているオプジェクトを安全 クトをコピーするというものです。これを保証した場合 , 指しているオプジェクトに対応するスマート・ポイン 適用可能な戦略のうち , 最も簡単なものは , スマート・ポインタをコピーする際は , 必ず指しているオプジェ 以下のような素直なコピー・コンストラクタを実装することは誤りなのです : pointee-(new T(*0ther. pointee—)) SmartPtr(const SmartPtr& other) public : class SmartPtr template く class T> smartPtr3 s m a 「 tP 2 smartPtr1 図 7.1 ディープ・コピー方式を用いたスマート・ポインタのメモリ・レイアウト po i ntee po i ntee po i ntee
167 第 7 章 スマート・ポインタ ト・ポインタは , 異なった型のオプジェクトに対するものであっても , 共通のコードを数多く含む傾向にありま スに関して , 通常のポインタを模倣するような 0 + のクラスです。しかし , それだけではありません。スマー スマート・ポインタとは何でしようか ? スマート・ポインタとは , シンタックスとある種のセマンティック 7.1 スマート・ポインタいろいろ SmartPtr の設計根拠 , およびその使用 , 微調整 , 拡張の方法も理解することができるでしよう。 に 1 つづつ実装を提示しています。そして最後に , 全てのパーツを集めた実装を行っています。これによって , この章では , SmartPtr ジェネリック・クラス・テンプレートを実装しています。各セクションでは , 個別 ・マルチスレッドにおける問題 ・テストと比較 暗黙の変換 ・所有権の管理戦略 ・スマート・ポインタの長所と短所 この章を読み終えれば , スマート・ポインタにまつわる以下のような問題のエキスパートになれるでしよう : ト・ポインタになります。 smartptr は , ポリシー ( 第 1 章 ) に基づいて設計されているため , 安全性や効率性の高い , 使用の容易なスマー この章では , スマート・ポインタの考察だけでなく , SmartPtr クラス・テンプレートの実装も行っています。 ます。 よって , 指しているオプジェクトの寿命を注意深く管理するという面倒な作業からアプリケーションを解放し ンタのシンタックスとセマンティックスに加えて , メモリ管理やロックといった処理を内部で実行することに て単純なポインタとして使えるようにした C+ + のオプジェクトです。スマート・ポインタは , れつきとしたポイ 手つ取り早く言えば , スマート・ポインタとは , 。 perato て→と単項演算子 operator * を実装することによっ りやすいエラーから些細なエラー , そして身の毛もよだつようなものまで ) についても考察しています。 マート・ポインタを様々な観点から ( 最も単純な点から最も複雑な点まで ) 考察し , 実装時の様々なエラー ( 判 くのシンタックス上やセマンティックス上の問題を混ぜ合わせた興味深いものとなっています。この章では , ス てきました。スマート・ポインタは , おそらく最も一般的で , 複雑かっパワフルな C+ + のイディオムであり , 多 今まで , 世界中のプログラマや開発者の手によってコードの山とインクの川がスマート・ポインタに捧げられ
168 す。このため , 既存の高品質なスマート・ポインタでは , によってテンプレート化されています : template く class T> class SmartPtr public : たいていの場合 , explicit SmartPtr()* pointee) : pointee— (pointee) ; SmartPtr& operator=(const SmartPtr& other) ; -SmartPtr() ; T& operator* ( ) const return *pointee T* operator—>() const return pointee— private : T* pointee— smartptr く T> は , T へのポインタをメンバ変数 pointee ーに集約します。 ンタによって行われていることです。スマート・ポインタによっては , データに対する何らかのハンドルを集約 しておき , 実行時にポインタを算出するような場合もあります。 インタを適用する原動力となります。しかし , すぐに判るように , スマート・ポインタは無料ではないのです。 ドの変更を最小限に抑えられるというのは , とても魅力的であり , 巨大化するアプリケーションにスマート・ポ インタの売り文句なのです。従って , 簡単にスマート・ポインタの恩恵にあずかることができるわけです。コー 変更を与えることなく , ポインタ定義をスマート・ポインタの定義に置き換えられるというのが , スマート・ポ sp の定義を見なければ , それがポインタでないとは誰も思わないでしよう。アプリケーション・コードに大きな sp->Fun() ; SmartPtr く Widget> sp(new Widget) ; void Fun() ; public : class Widget ます。つまり , 以下のような記述ができるのです : SmartPtr におけるポインタ・ライクなシンタックスとセマンティックスは , 2 つの演算子によって提供され 第 7 章スマート・ポインタ 以下のコードのようにポインタの型 これが , ほとんどのスマート・ポイ
7.13 スマート・ポインタとマルチスレッド 197 の呼び出しを発行したとしましよう (Widget には DoSomething という関数が定義されているものとします ) : SmartPtr く Widget> sp sp->DoSomething() ; 以下がトリックです。 SmartPtr の operator-> は , 一時オプジェクト LockingProxy く T> を返します。そして コンパイラは , operator-> の適用を繰り返します。すると , LockingProxy く T> の operator-> は , Widget* を 返します。コンパイラは , この Widget へのポインタを用いて D 。 s 。 mething への呼び出しを発行します。呼び 出しの間 , 一時オプジェクト LockingProxy く T> は生存し続け , 該当オプジェクトをロックすることになるため , オプジェクトは無事にロックされるわけです。そして , D 。 s 。 methi Ⅱ g の呼び出しから帰ってくると同時に 時オプジェクト LockingProxy く T> が破棄されるため , Widget オプジェクトのロックも解放されることになり ます。 自動ロックは , スマート・ポインタの階層化における良い適用例と言えます。 storage ポリシーを変更するこ とによって , このようにスマート・ポインタの階層化を行うことができるのです。 7.13.2 管理上のテータ・レベルにおけるマルチスレッド スマート・ポインタでは , 指しているオプジェクト以外のデータを操作する場合があります。セクション 7.5 で見ていただいたように , 参照カウント方式のスマート・ポインタは , 参照カウンタを共有しています。参照カ ウント方式のスマート・ポインタをあるスレッドから別のスレッドにコピーした場合 , 2 つのスマート・ポイン タは同じ参照カウンタを指すようになります。もちろん , どちらもユーザからアクセス可能であり , ロックする こともできる同じポインタ・オプジェクトを指すわけです。一方 , 参照カウンタはユーザからアクセス不能であ るため , その管理は完全にスマート・ポインタに任されます。 マルチスレッドに起因する危険にさらされるのは , 参照カウント方式のポインタのみではありません。内部 で共有データとしてポインタを互いに保持しあっている , 参照リンク方式のスマート・ポインタ ( セクション 7.5.4 ) も同様です。参照リンクによって形成されるスマート・ポインタのコミュニティは , 必ずしも同じスレッ ドに属しているものばかりとは限らないのです。従って , 参照リンク方式のスマート・ポインタをコピー , 代 入 , 破棄する度に , 適切なロックを発行しなければならず , それを怠った場合には双方向リンク・リストが崩壊 してしまうわけです。 結論として , マルチスレッドの問題はスマート・ポインタの実装に影響を与えるのです。では , 参照カウント 方式 , および参照リンク方式におけるマルチスレッド問題の解決方法について見てみることにしましよう。 7.13.2.1 マルチスレッド参照カウント方式 スレッド間でスマート・ポインタをコピーする場合 , 異なったスレッドから予測できないタイミングで参照カ ウンタがインクリメントされることになります。 付録で説明しているように , 値のインクリメントはアトミック ( 分割不能 ) な操作 (atomic 叩 era 。〃 ) ではありません。マルチスレッド環境における整数値のインクリメントやデクリメントを行うには , ThreadingM0deI く T> : : IntType 型と AtomicIncrement 関数や AtomicDecrement 関数を使わなければな りません。
第 7 章 void NonConstFun() ; void ConstFun() const ; public : class F00 T* operator—> ( ) { return pointee— 176 スマート・ポインタ SmartPtr く F00> sp; sp->ConstFun() ; sp->NonConstFun() ; / / operator-> を起動した後 , ConstFun を呼び出す / / operator →を起動した後 , NonConstFun を呼び出す どちらの関数が呼び出される場合でも , 同じ。 perat 。 r →が起動されるため , スマート・ポインタにはコピーを 行うかどうかを見極める糸口が無いのです。指しているオプジェクトに対する関数起動は , スマート・ポインタ の手の届くところを超えた場所で行われるわけです。 ( セクション 7.11 では , const, およびスマート・ポイン タやそれが指しているオプジェクトとのやり取りについて解説しています。 ) COW 方式が有効になるのは , できあがっているクラスの実装を最適化するような場合です。つまり , COW 方式のセマンティックスを効果的に実装しようとした場合 , スマート・ポインタではレベルが低すぎるのです。 もちろん , COW 方式をクラスに実装する場合 , スマート・ポインタは良い構成技術になり得ます。 この章における SmartPtr の実装では , COW 方式のサポートを行っていません。 7.5.3 参照カウント方式 参照カウント方式は , スマート・ポインタで用いられている最も一般的な所有権戦略です。参照カウント方式 では , 同じオプジェクトを指しているスマート・ポインタの総数を追跡します。その数値がゼロになった場合 , 指しているオプジェクトが破棄されます。この戦略は , 例えば , 同じオプジェクトを指すために , 通常のポイン タとスマート・ポインタを混在させないといった , ある種の規則が守られている限り , 非常にうまく動作します。 実際のカウンタは , 図 7.2 で示されているような構造によって , スマート・ポインタ・オプジェクト間で共有 されることになります。それぞれのスマート・ポインタには , オプジェクト自身へのポインタに加え , 参照カウ ンタへのポインタが保持されます ( 図 7.2 における pRefC 。 u Ⅱ t ー ) 。通常の場合 , これによってスマート・ポイン タのサイズが倍増するため , ーズや制約によっては受け入れられないオーバーヘッドとなる場合もあります。 もっと些細なことですが , 他にもオーバーヘッドがあります。参照カウント方式のスマート・ポインタは , 自 由領域に参照カウンタを保持しなければなりません。多くの実装について言えるのですが , 第 4 章で考察したよ うに , 0 + がデフォルトとして提供している自由領域アロケータは , 小規模オプジェクトの割り当て時には驚く ほど遅く , かっスペースを無駄にするのです。 ( 参照カウンタは , たいていの場合 4 バイトを占めることになり ます。これは明らかに小規模オプジェクトです。 ) つまり , 利用可能なメモリ・チャンクを検索するアルゴリズ ムの遅さからスピードのオーバーヘッドが発生し , アロケータが各チャンク毎に付加する管理情報によってサイ ズのオーバーヘッドが発生するのです。
7.13 スマート・ポインタとマルチスレッド 195 によって提供される以上のものが提供されるのです。そして追加のオーバーヘッドも , ほとんどの場合無視でき る程度となります。 しかし「ほとんどの場合」は「常に」ではありません。本格的なべクタを使用する必要もなければ , そのつも りもない場合が数多くあるはずです。つまり , 動的割り当て配列が正に必要な場合です。こういったケースにス マート・ポインタの能力が発揮できないというのは , 格好の良いことではないでしよう。 std : : vector と動的 割り当て配列の間には , 確かにギャップが存在します。スマート・ポインタは , ユーザの必要に応じて配列のセ マンティックスを提供することによって , このギャップを埋めることができるのです。 配列に対するスマート・ポインタという観点から見た場合 , デストラクタ中では delete pointee- の代わり に delete ロ p 。 i Ⅱ tee ーを呼び出すという点が唯一の重要な問題になります。この問題は , 既に 0wnership ポ リシーで取り組んでいます。 副次的な問題として , スマート・ポインタに対する operator 口をオーバーロードすることによるインデッ クス・アクセスの提供ということがあります。これは技術的には実現可能であり , 実際 , smartptr の初期バー ジョンでは配列セマンティックス用のオプショナル・ポリシーを別途提供していました。しかし , スマート・ポ インタが配列を指すのは , 非常に希なケースです。そしてこういったケースでは , Get 工 mpl を用いて , インデッ クス・アクセスを提供する方法が既に存在しているのです : SmartPtr く Widget> sp / / sp が指している 6 番目の要素にアクセスする Widget& 0bj = GetImp1 (sp) [ 5 ] ; 利便性を追求するあまり , 新たなポリシーを導入してまでシンタックスを追加するために格闘するというのは , あまり良い考えではないでしよう。 smartptr は , 0w Ⅱ ers p ポリシーを経由して破棄をカスタマイズする機能を提供しています。従って , 配列 に特化した破棄を delete ロ経由で行うことができるのです。しかし , smartptr はポインタ演算を提供してい ません。 7 ユ 3 スマート・ポインタとマルチスレッド スマート・ポインタは , オプジェクトの共有に役立つ場合がしばしばあります。そして , マルチスレッドの問 題は , オプジェクトの共有に影響を与えます。このため , マルチスレッドの問題は , スマート・ポインタにも影 響を与えるのです。 スマート・ポインタとマルチスレッド間のやり取りには 2 つのレベルがあります。 1 つは指しているオプジェ クトのレベルであり , もう 1 つは管理上のデータ・レベルです。 7.13 ユ指しているオブジェクトのレベルにおけるマルチスレッド 複数のスレッドが同じオプジェクトに対してスマート・ポインタ経由でアクセスを行う場合 , operator-> を 通じて行われる関数呼び出しの間は , 該当オプジェクトをロックすることが望ましいでしよう。スマート・ポイ ンタが生のポインタを返すのではなく , 代理オプジェクトを返すようにすることで , こういったことが可能にな ります。スマート・ポインタが指しているオプジェクトは , 代理オプジェクトのコンストラクタによってロック され , デストラクタによってロックが解除されるわけです。このテクニックは , Stroustrup ( 2000 ) で解説され
172 第 7 章スマート・ポインタ 最後に , スマート・ポインタは指される型も一般化できるようになっているべきです。これを行うため , SmartPtr は Storage ポリシー中にある , 格納型 , ポインタ型 , 参照型という 3 つの型を抽象化することにな ります。 SmartPtr に対する特定の実体化で , 全ての型が意味を成している必要はありません。このため , 極端 なケース ( ハンドルのようなケース ) では , operator-> や operator*, あるいはその双方に対するアクセスを 無効にするようなポリシーもあり得るのです。 7.4 スマート・ポインタのメンバ関数 スマート・ポインタの実装で現在多く見られるものには , スマート・ポインタの指しているオプジェクトにア クセスするための Get , それを変更するための set , 所有権を受け渡しするための Re1ease といったメンバ関数 を通じた操作が許されています。これは , smartptr の機能をカプセル化するための明快かつ自然な方法です。 しかし経験的に見ても , メンバ関数というものはスマート・ポインタに適したものではないのです。その理由 こういったメンバ関数 , およびスマート・ポインタの指しているオプジェクトのメンバ関数が , きわめて混 は , 乱しやすいものとなるからです。 例えば , Acquire や R1ease といったメンバ関数を保持した printer クラスがあると考えて下さい。 Acquire によって , プリンタの所有権が取得され , 他のアプリケーションから印刷できないようになり , Re1ease によっ てその所有権が放棄されます。 Printer へのスマート・ポインタを使用した場合 , セマンティックス上は全く 違っているものが , シンタックス上で奇妙な類似性を持ってしまうことに気付かれると思います : SmartPtr く Printer> spRes / / プリンタの使用権を取得する spRes->Acquire ( ) ; ドキュメントを印刷する spRes—>Re1ease ( ) ; spRes . Re1ease() ; / / プリンタの使用権を放棄する / / プリンタへのポインタを解放する SmartPtr のユーザは 2 つの異なった世界にアクセスしています。つまり , スマート・ポインタの指しているオ プジェクトが保持するメンバ世界と , スマート・ポインタが保持するメンバ世界です。ドット (. ) を記述する か矢印 ( ー > ) を記述するかによって , 2 つの世界が区別されているのです。 0 + を使用していると明らかに , シンタックス上のちょっとした違いにも常に注意を払うようになってきます。 C+ + を学ほうとしている Pascal プログラマは , & と & & というちょっとしたシンタックス上の違いにさえ嫌悪の念 を抱くかもしれません。しかし C+ + プログラマは , こういったことでは驚かないのです。彼らは習慣によって , こういったシンタックス上の違いをすぐに区別できるようになっているのです。 しかしスマート・ポインタのメンバ関数は , そういった習慣を根底から覆すのです。通常の ( 生の ) ポインタ には , メンバ関数というものが存在しないため , C+ + プログラマの目は , ドットを用いた呼び出しと矢印を用い た呼び出しを見分けることに慣れていないのです。コンパイラは , こういったことには慣れています。つまり , 通常のポインタの後にドットを記述した場合 , コンパイラはエラーを発生させるのです。このため , ちょっと考 えれば判るように , そして経験によっても証明されているように sp. Re1ease() と sp->Re1ease() 双方が工 ラー無くコンパイルされ , 違った動作を行ってしまうと , いくら経験豊かな C+ + プログラマであっても迷惑に 感じるわけです。治療法は簡単です。スマート・ポインタは , メンバ関数を使ってはいけないのです。つまり , SmartPt ては非メンバ関数のみを使用するわけです。こういった関数はスマート・ポインタ・クラスの friend としておくことになります。
7.2 取引 7.2 取引 169 しかし , スマート・ポインタで何がもたらされるのでしようか ? あなたはそう考えるに違いありません。単 純なポインタをスマート・ポインタで置き換えるメリットとは何なのでしようか ? 答えは簡単です。スマート・ ポインタには , 単純なポインタにはない値のセマンティックスが存在するのです。 値のセマンティックスが存在するオプジェクトとは , コピーや代入が可能なオプジェクトのことです。 int 型 こういったファースト・クラス・オプジェクトの完璧な例です。整数値は生成 , コピー , 変更を自由に行う は , ことが可能です。バッフア中の繰り返しに用いるポインタにも , 値のセマンティックスが存在します一バッファ の先頭を指すように初期化し , 末端に到達するまで動かしていくわけです。その最中に , 一時的な結果を保持す るため , ポインタ値を他の変数にコピーすることもできます。 しかし , new によって割り当てられた値を保持するようなポインタの場合 , 話は大きく違ってくるのです : Widget* p = new Widget ; いったん上記のように記述してしまうと , 変数 p は何かを指すものというだけではなく , Widget オプジェクト のために割り当てられたメモリを所有するものにもなるのです。このため , ⅵ dget オプジェクトの破棄を行い , そのメモリを解放するために , 後で delete p を発行するという責任が発生します。しかし , この行以降に以下 の行を記述した場合 : / / p に何か他のものを代入する p は , 以前に指していたオプジェクトの所有権を失い , 二度とそれに到達できなくなってしまいます。このため , リソース・リークが発生してしまうわけです。 さらに , p を他の変数にコピーしても , コンパイラはポインタが指しているメモリの所有権を自動的に管理し てくれないのです。この結果 , 2 つのポインタが同じオプジェクトを指すことになってしまいます。こういった オプジェクトを二重に削除してしまった場合 , その影響は削除しない場合よりも破滅的なものとなるため , 細心 の注意を払って所有権の追跡を行わなければならないのです。つまり , 割り当てられたオプジェクトを指すポイ ンタというものは , 意のままにコピー , 代入できないため , 値のセマンティックスが存在しないことになるわけ です。 スマート・ポインタを使えば , こういった状況を改善することができます。ほとんどのスマート・ポインタに は , ポインタ・ライクな動作に加えて所有権管理機能が提供されています。スマート・ポインタは所有権がどの ように譲渡されるかを把握し , そのデストラクタを用いて , 正しく定義された戦略によってメモリ解放を行うこ とが可能なのです。多くのスマート・ポインタには , 指しているオプジェクトの解放要否を決定できる , 十分な 情報が保持されているわけです。 スマート・ポインタは , 様々な問題分野に適用可能な各種の方法で所有権を管理することができます。スマー ト・ポインタによっては , 所有権を自動的に譲渡します。つまり , スマート・ポインタを他のオプジェクトにコ ピーすると , 元のスマート・ポインタが nu Ⅱとなり , コピー先がオプジェクトを指す ( そしてその所有権を持 つ ) ようになるのです。こういった動作は , 標準で提供されている std : : aut 。印 tr によって実装されています。 また , 所有権を自動的に譲渡しないスマート・ポインタとして , 参照カウント方式を実装しているものもありま す。これは , 同じオプジェクトを指しているスマート・ポインタの総数を管理し , その数がゼロになった場合 ,