定義 - みる会図書館


検索対象: Accelerated C++ : 効率的なプログラミングのための新しい定跡
226件見つかりました。

1. Accelerated C++ : 効率的なプログラミングのための新しい定跡

A. 1 ッ ec 、 : { 切〃 e ー夘 ec 、ト rage - c ss ー夘 ec 第・・第、 dec / ー s 〃 ec 、 } . 295 しかし、このような分類は、理解するための助けに過ぎません。宣言の中に対応する場所はなく、指定子はどの ようなものでも現われるからです。 型指定子宣言の基になる型名を定義します。組み込み型は A. 2 で議論します。 ←夘 ec 第、 . ・ char ー wchar-t ー b001 ー short ー int ー long ー signed unsigned ー float ー double ー void ー〃←れ ame ー const ー volatile 〃 e ー 7 田 me : class-name ー e 〃れーれ硯〃 e ー typedef-name (class-name はクラス名、 enum-name は列挙型 (enum) 名、カ司 7 田 me は typedef 名 ) const は、その型 のオプジェクトを変更しないという意味です。 volatile は、この変数が言語の定義の外で変更されるかもしれ ないということをコンパイラに教えるものです。この場合、あまり強力な最適化は避けるべきです。 const は型の意味を変えるために型指定子の一部であるのですが、 const なポインタを定義するため、定義子 の一部にもなることに注意してください。しかし、定義子としての const には常に * が続くので、あいまいな点 はありません。 記憶クラス指定子これは変数のメモリ上の位置や寿命を特定するためのものです。 s カ 0m9 ← c ss ー s 〃 e 朝、 s. ・ register ー static ー extern ー mutable register は、可能ならオプジェクトをレジスタに置いて、パフォーマンスを最適化するようにコンパイラに指 示するものです。 普通は、ローカル変数は宣言されたプロックから出るときに破棄されます。 static を付けておくと、そのス コープを出入りしても保持されるようになります。 extern は、現在の宣言が定義でないことを示しています。その場合、対応する定義はどこかにあるというこ とです。 mutable はクラスのデータメンバにだけ適用でき、それにより const のオプジェクトであっても、それらを 変更可能にします。 他の指定子これは型に結びついていない性質を定義します。 0 ″肥 7 、 dec / ー夘 ec 、 : friend ー inline ー virtual ー typedef friend ( 12.3.2 、 13.4.2 ) は保護レベルを上書きします。 inline は関数の定義に使われ、可能ならそのコードをインライン展開するようにコンパイラに指示をします。 しかし、コードが展開されるときには、その関数の定義がスコープ内に入っている必要があります。そのため、 inline 宣言した関数の定義は、宣言と同じへッダファイルに入れて置くとよいでしよう virtual ( 13.2.1 ) はメンバ関数にのみ適用可能で、その関数が動的に結合され得るという意味になります typedef ( 3.2.2 ) は、ある型の同義語を定義します。

2. Accelerated C++ : 効率的なプログラミングのための新しい定跡

72 第 4 章プログラムとデータの構成 他の定義と同様に、構造体の定義は 1 つのソースファイルに重複して書くことはできない。そのため、通常、重 複インクルードを避けるようにヘッダを書く必要がある。 関数 : 関数を使うファイル内には、その関数の宣言が必要。また、定義は重複してはならない。宣言と定義は 同じ形である。 e / れ〃 c 〃 07 ト〃 ame ( param-dcls ) ; / inline ノ 7 ℃イ e 和〃 c 07 トれ ame (parm-decls) { / / 関数の定義はここに書く こで戻り値の型 7 ℃ e は関数が戻す値の型を意味し、パラメータの宣言四 ram 記 c なはコンマで区切られた 関数のパラメータの型のリストである。関数は先に宣言されていなければ、呼び出すことはできない。引数の型 は対応するパラメータの型と同じかそれに変換されるものでなければならない。戻り値の型がより複雑な場合、 関数の宣言や定義の形は違ってくる。これについては A. 1.2 に書いたので参照のこと。 関数名はオーバーロードできる。つまり、同じ名前でも異なる個数や型のパラメータを持つ関数を定義でき る。コンパイラは同じ型の const 付きの参照と const なしの参照を異なる型として区別する。 オプションとして関数の定義に inli Ⅱ e を付けることができる。これは関数呼び出しのオーバーヘッドを避け るため、可能な場合は、関数を呼び出している場所に関数の定義コードを展開するという意味である。ただし、 必要なら展開されるコードは定義コードを少し変形したものにもなる。これをするためにはコンパイラは関数の 定義を知っている必要がある。そのため inline 関数の定義はソースファイルではなくへッダファイルに書き 込む。 例外処理 : try { / / コード例外を投げる ( スローする ) かもしれないプロックの始めに書く。 } catch(t) { / * コード * / } try のプロックを終了し型 t に一致する例外の処理をする。 cat ch の後の中力ッコの何に は型 t の例外を処理するためのコードを書く。 現在の関数を終了し、例外の値 e を呼び出し側に投げる。 例外クラス : ライプラリはいくつかの例外のクラス ( 型 ) を定義している。それぞれがどのような例外を報告 するものかは名前から推測することができる。 logic—error domain—error invalid-argument length-error out-of -range runt ime—error overflow—error underflow-error range—error 工ラーを起こした原因についての報告を戻す e . what() ライブラリにある仕組み : 文字列 sl と s2 を辞書的順序で比較する。 s 1 く s 2 義 定 の の 数 数 関 関 throw e ;

3. Accelerated C++ : 効率的なプログラミングのための新しい定跡

282 / / 以前と同様 protected: 第 15 章文字絵をもう 1 度 static void pad(std: :ostream& os, wd_sz beg, wd_sz end) { while (beg ! = end) { 0 S くく + + beg; この関数は、空白を出力する。 stream と、出力する空白の数をコントロールする 2 つの値を引数に取ります。 display 関数は、 pad を呼ぶ必要がある場合、現在の列番号と最後の列番号の 1 つ後を引数として渡します。 pad はこの範囲を空白で埋めるのです。 pad の宣言では static キーワードを使いました。 13.4 で見たように、このようにすると、 pad は static なメンバ関数になります。 static なメンバ関数は、オプジェクトに付随していないという意味で、普通のメン バ関数とは異なります。 抽象基底クラスにメンバ関数を定義していることに驚くかもしれません。オプジェクトを作れないクラスにど うしてメンバ関数が付けられるのかと。しかし、派生クラスはみな基底クラスを部分として含むことを思い出し てください。派生クラスは、基底クラスで定義されたメンバ関数をすべて継承するのです。そのため、基底クラ スのメンバ関数は、派生クラスのオプジェクトの基底クラス部分として実行できるわけです。今の場合、定義 したメンバ関数は static なので、基底クラスのメンバへのアクセスは static なものに限られます。しかし、 static なものに限らず、 ( 普通の ) データメンバやメンバ関数を、抽象クラスに定義できることも覚えておい てください。そのような関数は、派生クラスのオプジェクトの一部となった基底クラス部分にアクセスするの です。 static なメンバ ( 関数であってもデータメンバであっても定義できます ) はグローバルに定義する名前を減 らすのに便利です。そして今の pad がよい例なのです。いろいろな抽象化で、 pad ( 「詰める」という意味 ) とい う名前が使われることが想像できます。たとえば、本書では、学生の名前を出力する場合の整形でも、 Picture と同様のことを考えました。もし Pi cture クラスで使うために、 pad をグローバルな関数として定義すると、 Student-info クラスのために pad という関数をグローバルに定義できなくなります。もちろん、逆も言えま す。そこで pad を static なメンバ関数とすることで、他のクラスでも同じ pad という名前 ( 動作は違う関数 になる ) を使えるようにしておくのです。それぞれのクラスが自分の中だけで pad を定義しておくなら、それら はプログラム中で独立していて、共存もできるわけです。 15.2.4 VCat-Pic クラス 2 つの絵をつなぐクラスの定義は難しくありません。まず VCat_Pic を考えましよう。 class VCat—Pic: public Pic_base friend Picture vcat (const Picture&, const Picture&) ; Ptr く Pic_base> top, bottom; VCat-Pic(const Ptr く Pic_base>& t , const ptr く Pic_base>& b) : top(t) , bottom(b) { } wd—sz width() const

4. Accelerated C++ : 効率的なプログラミングのための新しい定跡

226 第 12 章値のように振る舞うクラスオブジェクト 12 ー 9 ostream-iterator を使って str の出力演算子を定義し直してください。 こで、「 istream_iterator を使って str の入力演算子を定義し直してください」と質問しないのは、なぜだかわかりますか。 12 ー 10 12.1 で反復子 2 つを引数に取る str のコンストラクタの定義を考えました。このようなコンストラク タは vec クラスで有用だと思います。このようなコンストラクタを vec に付け加え、 copy の代わりにその vec のコンストラクタを使って str のコンストラクタを書き直してください。 12 ー 11 この課題にある関数を付け加えれば str クラスは、本書で文字列を扱っているすべてのプログラムで使 えるようになります。第 5 章の文字絵の関数の定義や 5.6 と 6.1.1 の split 関数の定義を書き換えてみ てください。 12 ー 12 2 つの反復子を引数に取る insert 関数を vec と Str に付け加えてください。 12 ー 13 配列のデータを vec のオプジェクトに代入できるような代入関数を定義してください。 12-14 string オプジェクトで vec を初期化するコードを書いてください。 12 ー 15 4.1.3 の read ー hw 関数は読み込みに使っているストリームを調べ、ファイルの最後に到達したのか無 効な入力に遭遇したのかを判断しています。しかし、 str の入力演算子はそのようなチェックをしません。 なぜでしようか。それはストリームを無効な状態にして、そのまま放置してしまうでしようか。

5. Accelerated C++ : 効率的なプログラミングのための新しい定跡

13.7 詳細 251 動的結合 : 関数を使っている実際の型に基づいて、実行時にその型の関数が選ばれる仕組み。動的結合は、実 質的には、ポインタか参照を通して仮想関数を呼び出すときに起こる。仮想関数の仮想という性質は継承される ので、派生クラスで virtual を書く必要はない。 派生クラスで仮想関数を再定義する必要はない。もし、仮想関数を派生クラスで再定義しなければ、もっとも 近い基底クラスのものが継承される。しかし、クラス定義内で宣言した仮想関数は、その定義を書かなければな らない。仮想関数を宣言して定義を書かないと、しばしば不可思議なコンパイル時工ラーになる。 オーバーライド : 派生クラスで、基底クラスのものと、同じ名前、同じ型で同じ数のパラメータ、 const のあり なしも同じ関数を定義すると基底クラスの関数が上書きされたことになる。これをオーバーライドという。この 場合、一般には戻り値も一致していなければならない。しかし、 13.4.2 で見たように、基底クラスの関数がク ラスのポインタ ( または参照 ) を戻す場合、派生クラスは派生クラスのポインタ ( または参照 ) を戻すことができ る。引数の型や数が一致しなければ、基底クラスと派生クラスの関数は、同名であっても無関係と考えられる。 仮想デストラクタ : 基底クラスのポインタに delete が使われてオプジェクトが破棄される場合、そのオプ ジェクトは派生クラスのものかもしれない。そのような場合、仮想デストラクタが必要である。もし、仮想デス トラクタで特別な処理が必要なくても、仮想デストラクタそのものは定義すべきで、その中身を空にしておけば よい class base { public : virtual -base ( ) { } 他の関数と同様に、仮想デストラクタの仮想という性質は継承されるので、派生クラスで仮想デストラクタを再 定義しなおす必要はない。 コンストラクタと仮想関数 : オプジェクトが生成されつつあるとき、それが派生クラスの一部としてであって も、その型は生成されつつあるクラスの型である。そのため、コンストラクタの中で仮想関数を使うと、その生 成されつつあるオプジェクトの型に静的に結合したバージョンの関数呼び出しになる。 クラスは他のクラスを friend ( フレンド ) に宣言できる。これにより、そのクラスの クラスの friend 宣言 : 全メンバ関数を friend にしたことになる。この性質は継承されたり伝播されたりはしない。つまり、 friend の friend があっても、それはもとのクラスの friend ではない。 クラスの個々のオプジェクトのメンバとしてではなく、クラスに付随するメンバとして存在 static メン / ヾ : する。 static なメンバ関数は stat ic なデータメンバにしかアクセスできない。 stat ic なデータメンバの実 体は、クラスに 1 つしか存在しない。これは、通常は、クラスのメンバ関数を定義するソースファイルで初期化 しなければならない。クラス定義の外で初期化するので、その際には名前は完全修飾で書く。つまり value-type C / ass ーれ佖 7 〃 e : : S 佖〃 C ー 7 〃〃わ、〃 0 / 〃 . e = value ; とすれば、これは、型が ? 第ん e イ e 、クラス名が class-name である static なメンバ static-membet 、 name を 盟ん 6 で初期化していることになる。

6. Accelerated C++ : 効率的なプログラミングのための新しい定跡

12.2 自動の変換 213 てよいのです。たとえば、これがどのように働くのか、デストラクタを例に考えてみてください。もし、デスト ラクタを付けてもする仕事はないはすです。一般に、デストラクタが必要でないクラスでは、コピーコンストラ str オプジェクトは値のようになります。つまり、 str オプジェクトをコピーすると、 こまででコンストラクタを定義し、コピー、代入、破棄もどうするか決めました。これらの関数のおかげで 12.2 自動の変換 クタと代入演算子もわざわざ書く必要はないのです 11.3.6 ) 。 オリジナルとコピーが独 型の変数に代入することもできます。 れます。たとえば、 i Ⅱ t 型の値で d 。 uble 型の変数を初期化することができます。また、 int 型の値を double 立したものになるわけです。すると、次の問題は型の変換です。組み込み型では、しばしば自動で型変換が行わ double d = 10 ; double d2; 10 ; d2 / / 10 を d 。 uble に変換し、それで d を初期化 / / 10 を d 。 uble に変換し、それを d に代人 今考えている str クラスでは、 const char* から str を生成するには、 / / 新しい値を s に代人 / / t を初期化 こでは const char* を引数に取るコンストラクタを明示的に使って s を生成してい / / s の生成 S " he110 " Str t " he110 " ます。また、次のように書くこともできます。 と書くことができます。 Str s("hello"); const char* の st てへの代入ではまさにこれが行われています。 s " he110 " ; とあるとき、実際にはコンパ タです。 str が必要な場所で const char * が使われていると、コンパイラがこのコンストラクタを使うのです。 str にはすでにそのようなコンストラクタがあったのです。それが、 const char* を引数に取るコンストラク です。これは引数が 1 つだけのコンストラクタを定義することで定義されるのです。 型への変換です。後者の変換は 12.5 で説明しましよう。より一般的なのは、今考えている他の型からの変換 クラスの型変換には 2 方向があります。 1 つは他の型からその型への変換であり、もう 1 つはその型から他の 換に基づいて変換するのです。 変換するかを示すものです。組み込み型のときと同様に、コンパイラは必要なときに値の型をユーザ定義の型変 変換 (userdefinedconversion) になるのです。ユーザ定義の型変換とは、クラスオプジェクトをどのように型 const char* を引数に取るコンストラクタが定義されているからです。このコンストラクタがユーザ定義の型 考えるべきだと思うかもしれません。しかし、幸い、何もする必要はないのです。それは、 Str にはすでに str に新たに const char* を引数に取る代入演算子を定義し、コピーコンストラクタのオーバーロードも 上の 2 つの例は、どちらも const str& の来るべきところに、文字列リテラルを使っているのです。 算子を意味します。その代入演算子はコンパイラが生成するものです。この引数も const Str& です。つまり、 に取るコピーコンストラクタを使います。 2 番目のステートメントは定義ではありません。 こでは = は代入演 最初のステートメントは t の定義であり、 = は初期化を意味します。この形の初期化は常に c 。Ⅱ st str& を引数 11.3.3 で説明しましたが、 = という記号には上のように異なる 2 つの意味があることを思い出してください。

7. Accelerated C++ : 効率的なプログラミングのための新しい定跡

細 の 五ロ 296 A. 1.2 定義子 宣言内では 1 つの定義子に 1 つの「もの」しか対応させれらません。その「もの」に名前を付け、非明示的に 記憶クラスを決め、また指定子により他の性質も決めます。指定子と定義子のペアで、その名前がオプジェクト なのか、配列なのか、ポインタなのか、参照なのか、または関数なのかが決まります。たとえば、 int *x, f(); は、 x を int へのポインタとし、 f を int 型の値を戻す関数として宣言しています。 x と f の違いを示している のは、定義子の *x と f ( ) なのです。 虎 c ra 广ー * / const ノー & ノ . 市 7 ℃ c dec 川ん 7 、 市 7 ℃記 ec 帰 ra dec ra 7 、記ー ( declarator ) ー 市 rec カ記 ec ra ん 7 、 ( 佖 ram 礎、記 ec ra 〃 0 〃 s カ ) ー 市 c な dec ra ん 7 、 [ co 〃 s 〃 e 7 ℃ ss れ ] 虎 c ra ん記は e 襯 et 、 ( 識別子 ) とよばれるもので、修飾されることもあります。 dec ra 7 、 : / 〃 es d ー〃 arn ←夘 ec er 、ノ et 第 er ・ 定義子が、虎 c ん ra 7 、記のみから成る市 7 ℃ c 虎 c ra 7 、 ( 直接定義子 ) である場合、識別子は挈 ec 、が示す 性質を変更されることなく持っていることになります。たとえば、 int n; とあったとき、定義子はⅡです。これは直接定義子であり、それはただ 1 つの c 扉。減 d から成ります。 れにより、 n が型 int を持っことがわかります。 定義子が他の形態である場合、識別子の型は次のように決められます。まず、 T が、 friend や static のよ うなものでなく、指定子によって示される型であるとします。それから D が定義子とします。それなら、 D が 虎 c 7 0 ル記になるまで、次のステップを繰り返します。このとき、変形されていった T が探している型を表 すことになります。 1. D が (DI) という形をしているなら、 D を DI で置き換える。 2. D が * DI や *const DI という形をしているなら、 const のあるなしにしたがって、 T を「 T へのポインタ」 か「 T への const ポインタ (const な T へのポインタではない ) 」に置き換える。それから D を DI で置き 換える。 3. D が DI aram 6 ル虎 c ra 0 ル騰 t) という形なら、 T を切 ararn “記 ec ra 0 ルを引数に取り T を戻 す関数」に置き換える。それから D を DI に置き換える。 4. D が D [ c 。〃 s ね 7 ル e 7 ℃ ss 司という形なら、 T を「 T の配列」に置き換える。その要素数は c 。厩ー e 7 ℃ ss れで与えられる。それから D を DI で置き換える。 5. 定義子が & D 1 という形なら、 T を「 T への参照」に、 D を DI に置き換える。

8. Accelerated C++ : 効率的なプログラミングのための新しい定跡

12.7 詳細 friend: 自由にアクセスできるようになる。 template く class T> class Thing { メンバであるテンプレート関数 : クラスはテンプレート関数をメンバ関数にできる。その場合、クラスそのも のはテンプレートであってもなくてもよい。テンプレートメンバ関数を持つクラスは、同じ名前の関数群を効率 この宣言はクラス定義のどこにあってもよい。 friend と宣言されたものは、 private のメンバにも 225 friend std: : istream& operator>>(std: : istream&, 13.4.2 で説明するように、クラスも friend にできる。 Thing&) ; 的に持っことになる。テンプレートメンバ関数の宣言・定義は、他のテンプレート関数と同じ。 string の関数 : s . c-str() s . data() s . COPY(P, 課題 ヌル文字が最後についた文字配列を指す const char* を戻す。配列内のデータは、次の string の関数が s を変更するまで有効。ユーザは delete を実行してはいけない。また、そ のポインタの指すオプジェクトは短命なのでポインタも保持しておかないこと。 s . c-str() に似ているが、戻す文字配列の最後にヌル文字はない。 s の保持する文字を、 p が指すメモリ上へⅡ文字コピーする。ユーザは、自分で、 p が n 文字 を保持できるメモリ領域を指すようにしなければならない。 12 ー 0 この章のプログラムをコンパイルし、実行し、試してください。 12 ー 1 メモリを自分で管理するように str の定義を直してください。たとえば、文字の配列とそのサイズをデー タメンバにしてみてください。このようなデザインの変更で、コピー管理はどうなるでしようか。また、 vec を使うときのコスト ( たとえば、メモリ管理のオーバーヘッドなど ) も考えてください。 12 ー 2 c-str 、 data 、 copy 関数を書いてください。 12 ー 3 str に比較演算子を定義してください。そのときく cstring> にある strcmp 関数が役に立つでしよう。こ れは 2 つの文字ポインタを引数に取る関数で、前のポインタが指すヌル文字で終わる文字配列が、後のポイ ンタが指すものより小さければ負の値、同じなら 0 、大きければ正の値を戻す関数です。 12 ー 4 str に = = 演算子と ! = 演算子を定義してください。 12 ー 5 str に const char * からの型変換を使わない文字列結合演算子を定義してください。 12 ー 6 str が非明示的に条件に使えるように、型変換演算子を定義してください。これは str が空のとき偽とな りそうでないときは真となるものにします。 12 ー 7 標準の stri Ⅱ g クラスには、オプジェクトの文字を操作するために、ランダムアクセス反復子があります。 反復子を定義し、先頭と末尾を指す反復子を戻す関数を str に定義してください。 12 ー 8 str クラスに getline 関数を付け加えてください。

9. Accelerated C++ : 効率的なプログラミングのための新しい定跡

162 の関数は読み込みアクセスだけなので、 const 宣言をしました。 第 9 章新しい型を定義する grade と read の定義は、クラス定義の外に書きました。 name のようにメンバ関数をクラス定義の一部とし て定義すると、コンパイラに「可能ならば関数呼び出しのオーバーヘッドを避けてインライン展開 ( 4.6 ) する ように」と指示することになります。 Ⅱ ame のような関数はしばしばアクセス関数 (accessorfunction) と呼ばれます。この関数は実際にはデー タ構造の一部にのみアクセスを許可する関数なので、この名前はやや誤解を生むかもしれません。実際、歴史 的に、隠蔽されたデータへのアクセスを許し今まで作り上げてきた情報のカプセル化を破壊する関数がしば しば使われてきました。しかし、 こでいうアクセス関数は、クラスの抽象インタフェースの一部なのです。 Student-info の場合の抽象とは、学生とその学生の成績の表現を意味します。学生の名前の表現も抽象の一部 であり、それがⅡ e 関数なのです。一方、 midterm 、 final 、 h 。 mew 。 rk にはアクセス関数を付けません。これ らは、内部の詳細であり、インタフェースの一部ではないのです。 Ⅱ ame 関数を付け加えたので、 compare 関数を書くことができます。 b001 compare (const Student—inf0& x, const Student—info& y) return x . name() く y. name(); この関数は 4.2.2 のものによく似ています。唯一の違いは、学生の名前をどのように取り出しているかだけで す。オリジナル版では、データメンバを直接使っていました ーでは、学生の名前を戻す name 関数を使っ ています。 compare もインタフェースの一部なので、この関数の宣言も stude Ⅱ t ー i Ⅱ fo を定義するヘッダファ イルで宣言しておかなければなりません。また、その定義は student _inf 。のメンバ関数の定義があるソース ファイルに書きます。 9.3.2 空でないか確かめる メンバを隠蔽し、適当なアクセス関数を定義しましたが、まだ 1 つ問題が残っています。まだ、ユーザがオプ ジェクトのデータを直接チェックしたいと考える場合があるのです。たとえば、次に示すように read を呼ぶ前 のオプジェクトに対して grade を使ったら何が起こるでしよう。 Student_info s ; / / 例外 : s にはまだデータがない cout くく s . grade() くく endl; s にデータを入れるための read を呼んでいないので、 s の homework は空です。そこに grade を呼べば例外を 投げてしまいます。もちろん、ユーザが例外を catch することもできますが、問題の発生を事前に知り、関数 呼び出しを止める手立てはないのです。 第 4 章の student ー i れ fo 構造体を使っていれば、ユーザは grade を呼んでよいかどうか確認することができ ました。それは homework が空かどうかを直接確認したのです。この方法は有効ですが、これを実行するため にはユーザにデータ構造の詳細を見せることになります。実は、もっと抽象的なチェック関数を作ればよいの class Student—info { public :

10. Accelerated C++ : 効率的なプログラミングのための新しい定跡

294 細 詳 の 五ロ ニ = ロ 付 このような宣言は次のように書いてはっきりさせることもできます。 int* p; int q; また、 / / analysis ー fp を関数のタイプ名として定義する / / その引数の型は c 。Ⅱ st vector く student_info>& で、戻り値の型は d 。 uble typedef double (*analysis—fp) (const vector く Student_info>&) ; analysis—fp get—analysis—ptr() ; 残念ながら、これも、他人が書いたプログラムの複雑な宣言を読む助けにはなりませんね。 一般に宣言ステートメントは次のような形をしています。 dec ra 0 れ - m た・ specifier[ declarator ー加 / た et 、ガん declar 、 / た er これはそれぞれの売 c ra 7 ・・ ( 定義子 ) のところで名前を定義するものです。これらの名前は宣言された場所か らその宣言のあるスコープの最後までの間、意味があるのです。宣言が定義であることもあります。名前の宣言 は重複してもかまいませんが、定義は一度しか書けません。宣言が定義になるのは、それによりメモリが確保さ れる場合やクラスや関数の中身が定義されるときです。 い + は宣言構文を C から受け継いでいます。宣言を理解する上で重要なことは、これが型名とその属性を特定 する夘 ec 第、 ( 指定子 ) と、続く 0 個以上の虎 c 扉。 r ( オプションでそれに市託 7 、 ( 初期化子 ) が付きま す ) の 2 つの部分から成り立っているということです。虎 c ra ん 7 、は、それぞれの型名と宣言の形から、その型 の「もの」の名前を決めるのです。 宣言を理解するための最初のステップは、指定子と定義子の境界をはっきりさせることです。これは驚くほど 簡単です。指定子は、キーワードか型の名前です。そのため、指定子が終わるのは、そのどちらでもないものが 現われたときです。たとえば、 const Char * const * COIISt * cp; を考えましよう。キーワードでも型の名前でもない最初のものは * です。そこで指定子は、 、 ( 唯 const char で ーっの ) 定義子は * const * const * cp なのです。 もう 1 つ例を挙げましよう。 10.1.2 の不思議な宣言です。 double (*get-analysis-ptr() ) (const vector く Student_info>&) ; 境界ははっきりしています。 d 。 uble は型名で、それに続く ( はキーワードでも型名でもありません。そのため、 指定子部分は、 double だけです。そして定義子は、最後の ; を除く後の部分全部となります。 A. 1.1 指定子 s 世 c ( 指定子。より正確には「宣言指定子」 ) には、大きく分けて 3 つの分類があります。型指定子、記 憶クラス指定子、他の指定子 ( それぞれ、構文説明のなかでは、カ e - s 世 c 旅広 storage-class-specifiet 、、 0 e 売 c / ー夘 ec er 、と表記 ) です。