slist - みる会図書館


検索対象: プログラミング言語C++
34件見つかりました。

1. プログラミング言語C++

346 第 8 章テンプレート template く class T> class Sp1ist : private S1ist<void*> { public : void insert()* p) void append ( T* p ) T* get ( ) { return template<class T> { S1ist<void*>: :insert(p) { SIist<void*>: :append(p); } (T*) S1ist<void*>: :get( ) class 工 splist : private slist base { public : void insert()* p) { slist base: :insert(p) void append()* p) { slist base: :append(p); } T* get( ) { return (T*) slist base: :get( ) ′ これも型チェックを改善し , さらにコードの複製を減少させる . typedef S1ist< S1ist<date> > dates ー である . 例えば , 日付から成る疎な行列は次のように定義できるだろう : テンプレート自身の要素の型がテンプレートクラスになっていることは , しばしば有用 こでの空白の使い方に注意されたい . 最初と二番目の > の間の空白の省略は , typedef S1ist<SIist<date>> dates; の中の > > が右シフト演算子と解釈されたら , 構文ェラーを引き起こすだろう . 今までのよ うに , typedef によって導入された名前は , それが名付けた型の同義語であり新しい型 ではない . 他の長い型名に対してそうであるのとちょうど同じように , typedef は長い テンプレートクラス名に対して有用である . テンプレート中でいくっかの方法で使用されるテンプレート引数は , テンプレート引数 の中では一度だけ挙げられるべきであることに注意されたい . したがって , T オプジェク ト及び T のリストを使用するテンプレートは , 次のように定義される : template く class T> class mytemplate { T Obj; S1ist<T> slst;

2. プログラミング言語C++

350 第 8 章テンプレート slink* slist base iter: :operator( ) ( ) / / 反復の終わりを示すためには 0 を返す slink* ret ce ? ( ce=ce—>next ) if (ce cs—>last) ce return ret ー コレクションのクラスの friend と宣言しなければならない : これから SI 土 st 及び lslist の反復子は容易に構築できる . ます反復子を , これらの各 templat«class T> class template<class T> class friend class 工 slist template<class T> class template<class T> class lslist iter; lslist { iter<T> ー S1ist iter; S1ist { friend class S1ist iter<T>; 反復子の名前を , それらのテンプレートクラスを定義せずに導入している , そのやり方に 注意されたい . これがテンプレート間の相互依存を処理する方法である . 次に , 反復子が定義できる : template<class T> class lslist iter : private slist base iter { public : lslist iter( 工 s1ist<T>& s) slist T* operator( ) ( ) { return (T*) slist base iter: base iter(s) { } : operator ( ) ( )

3. プログラミング言語C++

340 第 8 章テンプレート void f (const char* s) slist base slb; s1b. insert(new name(s) 房 name* p = (name*)slb. get( 房 delete p; これは機能するが , slist base が name ではなく slink で定義されているため , slist base: :get( ) によって返される slink* を na 爪 e ☆に変換するための明示的な キャストを使用する必要がある . これはエレガントでない . s 1 ink から派生したたくさ んのリストやクラスを使用する大きなプログラムでは , それはエラーを起こしがちでもあ T* get( ) { return (T*) slist base: :get( ) VOid insert()* a) { slist base: :insert(a) ′ public : class lslist : private slist base { template く class T> る . 望まれるのは sl 土 st base の型安全なバージョンである : name* p = ilst. get( ilst. insert(new name(s) ) : lslist く name> ilst; void f (const char* s) ように使用することができる : 名前工 sl 土 st は。侵入的単方向連結リスト " を表している . このテンプレートは次の い . 安全ではない実装の詳細を , 偶然にでもユーザにいじり回して欲しくないからである . 全である . slist base は工 slist の p て土 n ▽ ate 基底クラスであることに注意された であることを保証しているため , 工 slist::get() の中でのキャストは全く合理的で安 クラス工 sl 土 st はリスト上のすべてのオプジェクトは確かに型 T 又は T から派生した型

4. プログラミング言語C++

リストテンプレート / / は name ☆を要求している lslist<name>: :insert( ) ilst . insert(e); / / 工ラー lslist<name> ilst; void g(expr* e) class expr : public slink { 誤用を試してみてもコンバイル時に捕えられる : delete p; 8.3 341 このメカニズムは 今までの例について注意すべき重要な事柄がいくつかある . 第一に (lslist 中のアクセス関数という非常に限られた文脈での愚かな間違いを除いて ) 型安全で ある . 第二に , lslist 中のアクセス関数は自明なインライン関数であるため , 型の安全生 は時間やスペースの浪費なしに達成される . 第三に , すべてのの仕事は slist base の一まだ示されていない一実装によって行われるため , コードの複製がなく , また実装 (slist base 関数 ) のソースコードをユーザに使えるようにする必要がない . これが商 業的に重要であると考える人もいる . ユーザのコードの再コンパイルを必要としない再実 装が可能になるように , インタフェースとその実装の分離も提供している . 最後に , 単純 な侵入的リストは時間とスペースの点で最適に近い . 言い換えればこの戦略は , 式の大い なる柔軟性と効率を提供する一方で , 時間 , スペース , データ隠蔽 , 及び型チェックにつ いて最適に近い特性を持っ . しかしながら , sl 土 nk から派生したオプジェクトに限りエ sl 土 st 上に存在し得る . れは , 土 nt の工 sl 土 st を持っことができないということ , 先に定義されており slink を 基底としていない何らかの型から成るリストを持っことができないということ , そして , 二つの工 sl 土 st 上に一つのオプジェクトを持つには何らかの作業を必要とするというこ とを含意する ( .5.1 ) .

5. プログラミング言語C++

8.3 template<class T> リストテンプレート 351 class S1ist iter : private slist base iter { public : S1ist iter(S1ist<T>& s) inline T* operator ( ) ( slist base iter(s) template<class T> T* S1ist_iter く T>: :operator( ) ( ) T1ink<T>* lnk (T1ink<T>*) slist base iter: return 1nk ? &lnk—>info : 0 ー : operator ( ) ( 房 ーっの基底クラスからクラスの族 ( すなわちクラステンプレート ) を派生させるトリック を再び使用したことに注意されたい . これは継承を , 共通点を表現し不必要なコードの複 製を防ぐために使用している . リストや反復子のような単純で頻繁に使用されるクラスの 実装において , コードの複製を避けることの重要性は , いくら誇張してもし過ぎるという S1ist iter<name> iter2 ( Ist2 while (p=iterl( ) ) { const name* p; lslist iter<name> iterl(lstl); Ist2 . insert(n); lstl . insert(n); S1ist<name> 1st2; lslist<name> lstl; void f(name* n) ことはない . こういった反復子は次のように使用できる : const name* q; while (q=iter2 ( ) ) { if ( p q) cout くく "found くく *p くく

6. プログラミング言語C++

8.7 派生とテンプレート 369 な変換が許されたならば , 型システムにおける落し穴に陥りやすくなるであろう . 例えば : VOid f (Vector<circ1e>* pc ) Vector<shape>* ps = pc ー SqUare ー / / ェラー : 型の不一致 / / 不適格なものを入れる 工 slist, TIink, SIink, Sp1ist, 工 slist iter, SIist iter, . 及びテンプレー ト sortablevector で示されたように , テンプレートがなければ , 類似したクラスの 派生は長ったらしく , したがってエラーを起こしがちになりかねない . 逆に派生がなけれ ば , テンプレートのイ吏用は , クラステンプレートのメンバ関数のコードの大量の複製 , ク ラステンプレートにおける宣言情報の大量の複製 , そしてテンプレートを使用する関数の 大量の複製を意味するだろう . 8.7.1 テンプレート引数を通じての実装の記述 コンテナクラスは , しばしば記憶領域を割り当てなければならない . 時には , 色々な領 域割り当ての方法の間から選んだり自前の方法を供したりする機会を , ユーザに与えるこ とが必要ー又は単に便利一である . これはいくつかの方法で行うことができる . ーっの 方法は , 求めているコンテナへのインタフェースと扣 .7.2 で述べられた配置の技法を用い る領域割り当て子のクラスから新しいクラスを合成するために , テンプレートを使用する ことである : template<class T, class A> class Contr011ed container public Container<T> ′ private A { void some function ( ) T* p new(): :operator new(sizeof(T) ) ) T;

7. プログラミング言語C++

8.3 。。薄切りにする " ことを拒むであろう : リストテンプレート 345 void g2 (S1ist<shape>& 01 土 st ′ const circle& c) クラステンプレートへの引数としてリファレンスを使用してはならない . 例えば : / / 良い plist. insert(&grin); void g3(S1ist<shape*>& plist, const smiley& grin) 薄切りの問題を避けるためにはポインタを使用しなければならない : / / 作成しようとした 01土St ・ insert(c); / / 工ラー : 抽象クラスのオプジェクトを void g4(S1ist<shape&>& て1土St . insert(grin) ー を引き起こす . この場合 S1ist: :insert(T&) ー を展開すると , 違法な宣言 S1ist: :insert(shape&&) ー rlist, const smiley& grin) / / 工ラー : 生成されたコードはリファレンス / / へのリファレンスを含む (Shape&&) このように用いられるリファレンスは , テンプレートが展開されたときにしばしば型工ラー いアイデアである : ポインタのリストは非常に便利であるため , それらに特定の名前を付けておくことは良 つことはできない . になる . リファレンスはオプジェクトではないため , リファレンスへのリファレンスを持

8. プログラミング言語C++

13.8 インタフェースクラス 13.8 インタフェースクラス 597 最も重要なクラスの種の一つは , つつましやかでそのほとんどが見渡せるインタフェー スクラスである . インタフェースクラスは多くのことはしない - ーもしそんなことをした ら , それはインタフェースクラスではなくなるだろう . それは単純に , 局所的な要求に対 するなんらかのサービスの外見を調整する . いつでもうまくすべての要求に等しくサービ スすることは原理的に不可能なので , インタフェースクラスは , 共有を許してもすべての 使用を共通の拘束衣に押し込めたりしないために , なくてはならないのである . インタフェースの最も純粋な形は , どのようなコード生成も引き起こさない . .3.2 か ら splist テンプレートを考えてみよう : template<class T> class Sp1ist : private SIist<void*> { public : ▽ 0 土 d insert()* p) { S1ist<void*>: :insert(p); } void append()* p) { S1ist<void*>: :append(p); } T* get( ) { return (T*) SIist<void*>: :get( ) こで splist は , 安全ではないが総称的な v 。土 d ☆ポインタを , はるかに有用である型 安全なリストクラスの族に変えている . インライン関数はインタフェースクラスを手頃な ものにするのにしばしば欠かせない . こういった場合 , 転送する (forwarding) インライ ン関数が型調整のみを行うのであれば , 時間やスペースのオーバーヘッドは加わらない . 当然ながら , 具体型によって実装されている抽象型を表現する抽象基底クラス ( い 3.3 ) は , 節い 3.9 のハンドルがそうであるように , インタフェースクラスの一形式である . し かしながら , , こで我々は , インタフェースを調整する以外に特別な関数を持たないクラ スに焦点を合わせよう . 多重継承を用いて二つの階層を併合する問題を考えてみよう . 名前の衝突があるとき , すなわち二つのクラスが全く異なる演算を行う仮想関数に対して同じ名前を用いていた場 何ができるだろうか . 例えば , ユーザとの対話が一般のウインドウクラスによって処 理される西部の荒野のビデオゲームを考えてみよう : class Window { virtual void draw( ) ー

9. プログラミング言語C++

344 第 8 章テンプレート ▽ 0 土 d f(name* p) ストを使用するというのが , 良いアイデアとなることが多い S1ist<name*> 1st2; lslist<name> lstl; lstl . insert(p); 1st2 . insert(p) : / / オプジェクト ' *p ' を通じてリンクする / / 'p' を保持するために別の / / リンクオプジェクトを使用する 当然ながら , そのようなトリックは普通 ( コンポーネント間のインタフェースで使用され るリストの型についての混乱を避けるため ) プログラムの特定のコンポーネントの中での み使うことができるが , それこそまさしく , 実行時の効率やコンパクトさを競うゲームを 行う価値がある場合である . S1ist コンストラクタ中の insert( ) への引数のコピーゆえに , S1ist は整数 , complex, そしてポインタのような小さなオプジェクトにのみ適している . コピーが高 価であったりセマンティクス上の理由で受け入れられない場合のオプジェクトについては , オプジェクト自身よりもむしろポインタをリストにつなげるのが良いアイデアであること が多い . これは上の f( ) の中で 1st2 について行われている . S1ist: :insert( ) への引数がコピーされるため , 基底クラスのオプジェクトを予期 している insert() 関数に派生クラスのオプジェクトを渡すことは , ( 素朴に ) 期待され るようには働かないであろう , ということにラ意されたい class smiley : public circle { / * 問題になっているクラスが抽象クラスならば , コンバイラは派生クラスのオプジェクトを こりそうな事例においては , コンパイラによって発見される , ということにラ主意されたい . smiley の circle 部分だけが格納されるだろう . この性質の悪い問題は , 最もよく起 olist . insert(grin); void g1(S1ist<circIe>& olist ′ const smiley& grin)

10. プログラミング言語C++

8.4 関数テンプレート void f( 工 slist<name>& ilst) / / 重複の愚直な探索 : list_iter<name> slow は lst / / 反復子を使用 name* p; while (p = slow()) { ilst. set_current ( p / / 現在の要素に依存 name* q ー while (q = ilist . next( ) ) if (strcmp(p—>string,q—>string) cout くく "duplicate もっと別のスタイルの反復子については 8.8 を見られたい . 8.4 関数テンプレート 353 テンプレートクラスの用法には , テンプレートメンバ関数が含まれる . 加えて , 大域的 な関数テンプレート , すなわちクラスのメンバではない関数テンプレートも定義できる . 関数テンプレートは , クラステンプレートがクラスの族を定義するのと同じゃり方で , 関 数の族を定義する . どのように sort( ) 関数を提供できるかという一連の例を通じて , のアイデアを探求してゆく . 以下の小節における sort( ) の各変形版は一般的な技法を説 明している . 今までと同様に , 議論の焦点はアルゴリズムの設計よりもむしろプログラムの構成であ るので , 自明なアルゴリズムが使用される . これらの sort ( ) テンプレートの変形版は , 言語の特徴と有用な技法を説明するために示される . 変形版は , それらが。どのくらい良 いか " に従って並んでいるわけではないし , 数多くの文脈で ( 比較を行う関数へポインタ を渡す ) テンプレートではない伝統的なバージョンについても多くが語られている . 8.4.1 簡単な大域関数テンプレート まず , 最も簡単な sort ( ) テンプレートについて考える :