質問: オブジェクト指向で「静的な型と動的な型」という用語が出てきたのですが、説明を読んでもわかりません。参考書には以下のように書かれているのですが、"式"というところで引っ掛かり結果よくわからないまま参考書を読み進めていましたが理解している前提での説明が続いてその先の理解できないので別の説明をしてもらいたく質問しました。 --- **静的な型** > > 「静的な型 式の型、その式がもたらす結果の型として実行時の意味を考慮ぜずにプログラムを解析することで得られる。式の静的な型は、その式が位置するプログラムの形だけから決まり、プログラムの実行中に変わることがない。」 > > > **動的な型** > > 「動的な型。左辺値式の表す左辺値が指す最派生オブジェクト型。(例) その静的な型がクラスBへのポインタであるポインタpがクラスBから派生したクラスDのオブジェクトを指していたとすると、式*pの動的な型はDとなる、参照も同様に扱う。右辺値式の動的な型は、それの静的な型とする。」 > > > --- ``` void put_member(const Member* m) { cout << (m->get_weight() >= 65 ? "〇" : " "); m->print();//ここです。 } int main() { Member m("mmm", 15, 75.2); VipMember v("aaa", 17, 89.2, "会費全額免除"); SeniorMember s("sss",43,63.7,3); put_member(&m); put_member(&v); put_member(&s); _getch(); return 0; } ```
回答: 一般に静的な型付けとはプログラマがソースコード内に型を宣言して記述するものです。 (それはautoやvarなどの型推論も含む、型推論は初期化値である右辺値から型を推論し左辺値の型とせよというプログラマからの宣言であると解釈できる。) 静的な型付けがされた変数にはその型に適したものしか格納できないようになります。 int型変数に文字列を代入することはできません。 型を宣言するということは、変数に対してそうした制約を課する行為であるといえます。 ソースコードをコンパイラに渡した時点でその変数は格納できるものが決まっているわけです。 (これを静的な型とここでは呼ぶことにします。プログラマが指定する型です。) 一方で世の中には型を宣言しなくても動作する言語がありますよね(javascriptとか) これはプログラマが型を宣言しなくても数値と文字列が区別されます。 そのギミックについて理解するには、そうした言語では変数(左辺値)が型を持つのではなく値(右辺値)自体が型を持っていると考えればいいのです。 たとえばjavascriptにおいては、var x = "abc";と記述すると、xには{type:"String",value:"abc"}というような型を持った値が格納されるとイメージすればすんなり理解できます。 そこにさらにvar x = 1;と記述するとxには{type:"Number",value:1}といった値が格納されると考えると、xに型宣言をする意味自体がないわけです。型宣言しなくてもコンピュータは実行するに十分な情報を獲得できます。 (これを動的な型と呼ぶことにします。値自身が保持する型です。) さてC++におけるオブジェクト指向についてですが、オブジェクト指向には継承やポリモーフィズムという概念があることはご存知だと思います。 継承の難しさは宣言された型と、値自身が保持する型が必ずしも一致しないということになります。親クラスのポインタで派生クラスのオブジェクトを指してもエラーにはなりません。 動的な型が確定するのはいつか?それはオブジェクトがコンストラクタによって生成された時点でしょう。事実上継承がある状態においては型宣言はあまり意味をもちません。動的な型が実行時における挙動を決めます。 (もちろん明らかに誤った型宣言をコンパイラ時にエラーとして返すぐらいの効力はありますが) 継承というのは純粋な静的の型宣言を要する言語(C)と動的に型を解釈する言語(javascript)の中間にあるような機能だと捉えるといいです。C++はCを踏襲しているので、文法的に左辺値の型宣言を必要としますが、オブジェクト指向において実際の挙動を決めるのは右辺値に格納されている型なのです。