C++はCをベースとして、そこにオブジェクト指向をサポートする機構を追加した言語です。ほかのオブジェクト指向言語と比べて、以下に示すような特徴があります。
2および3の特徴が、Symbian OSがターゲットとする環境には欠かせません。大規模開発のためにオブジェクト指向アプローチが必要だけれども、しかし性能を保証できる言語である必要もある。そのようなわけでC++が採用されたのだと思われます。
では、C++は携帯、スマートホン向けのシステム記述言語における“銀の弾丸”なのでしょうか? ちょっと視点を変えて検討してみたいと思います。
C++では、クラスからオブジェクト(厳密にいうとクラスのインスタンス)を作成する方法は2つに大別できます。1つは、new演算子でオブジェクトをヒープ上に確保する方法。この場合、オブジェクトが不要になった際にdelete演算子で解放を行う必要があります。もう1つの方法は、別のメモリコンテキストの一部として領域を確保する方法です。自動変数として宣言し、スタック上に実体を確保する場合が該当します。
C++におけるメモリリークとは、newで確保されたオブジェクトが何らかの理由によりdeleteされないことにより発生します。「じゃあ、メモリリークしないように開発者の皆さんは忘れずにdeleteを呼んでください、よろしく」という話で終わらせてしまえるかというと、もちろんそんなことはありません。
C++では、ポインタの指している先がヒープなのかスタックなのかを知る一般的な方法がありません。これは結構深刻な問題です。いまポインタが指しているオブジェクトはdeleteで明示的に消す必要があるものなのか、それとも勝手に解放されるのでこちらで消すとまずいものなのか。この判断をプログラム上からできないことを意味します。
オブジェクトの後始末に関する処理が1カ所に集約されている場合を想定してください。一般にC++における後始末は、「終了処理をせよ」という指示をオブジェクトに出す部分と、必要であればオブジェクト自体の消去を行う部分の2つからなっています。ヒープ上のオブジェクトに対してはdeleteが必須です。逆に、スタック上のオブジェクトに対してdeleteを発行すると、プログラムは異常終了することになります。
あるクラスXのオブジェクトをヒープ上に取るかスタック上に取るかは、言語的には大した問題ではありません。しかし、交ぜて使うとこのように後始末処理などで混乱することになります。ポインタに属性がぶら下がっていて、deleteの要否が動的に判断できればよいのですが、C++には残念ながらそのような機能はありません。後始末を集約する目的は、例えばメモリリークがないことを保証するためなのですが、ここに至って本末が転倒してしまう可能性があることがお分かりいただけるでしょう。「本当にヒープにオブジェクトを取ってるんだよな? delete発行してもいいんだよな?」そんな疑念を解消する方法は、実はC++には備わっていないのです。
ではほかの言語ではどのようにクラスのインスタンスを作り分けているのでしょうか。
GC機能付きのOOPLでは、型を分別する物差しが古典的な言語とは異なっています。そのモノが置かれるメモリの種類によって分類をするわけで、これはかなり画期的な定義です。このアプローチは、まさにいま問題にしていることへの回答となり得る考え方です。
やっとSymbian OSにおけるC++の使い方にたどり着くことができました。実は、Symbian OSでもGC機能付きOOPLと同じく、クラスの種類をメモリの使い方で分けています。クラスを値型のインスタンスしか作らないものと、参照型のインスタンスしか作らないものに分けてしまえば、あるポインタがdeleteを必要とするのか否かは、クラスごとに一意に定まることになります。
これは誰が何のまねをしたということではなくて、オブジェクト指向を計算機資源(ヒープとスタックという非対称なメモリ環境を持つ)の上にインプリメントすることをマジメに考えたら同じ結果になったということだと思います。そういうものを一般に「安定した概念」といいます。ただしSymbian OSはC++を採用しているため、言語とやりたいことの間にギャップが発生することになります。
そこでSymbian OSでは、C++の言語仕様だけではカバーできない部分に関して、各種の規約(コンベンション)を導入することで解決を図っています。メモリの取り方も例外ではなく、以下のような規約が用意されています(注)。
分類 | 特徴 |
---|---|
Tクラス | クラス名は「T」から始まる必要がある(「Type」のT) ・ 単純な型 ・ スタック上またはクラス、構造体のフィールドにインラインに作ることができる ・ デストラクタを持ってはならない(デストラクタが必要となる構造はすでに「単純」ではない) |
Cクラス | クラス名は「C」から始まる必要がある(「Class」または「Compound」のC) ・ いくつかのクラスを要素に持つ複合的な型 ・ new(ELeave)演算子(次回解説)でヒープ上に作らねばならない ・ CBaseの派生クラスである必要がある |
表1 クラス分類 |
ポインタの型名を見れば、インスタンスのメモリ種別が分かることになります。先に述べたようなポインタの読み分け問題は、Symbian OS環境では無縁となります。
これ以外にもクラスの典型的な使われ方を表現するために、以下のようなクラス名に関する規約が存在します。
分類 | 特徴 |
---|---|
Rクラス | クラス名は「R」から始まる必要がある(「Resource」のR) ・ リソースを参照するクラス ・ リソースへのハンドルは十分小さいが、リソースの確保、解放という状態管理が存在するためTクラスと分別して考える必要がある |
Mクラス | クラス名は「M」から始まる必要がある(「Mixin」のM) ※Mixinは多分Symbolics社のFlavorsから ・ インターフェイスを定義するクラス ・ Javaでいうinterfaceとほぼ等価な機能を提供する |
表2 クラス分類(続) |
GCこそありませんが、最新のOOPLで使われている概念がSymbian OS環境にも存在することが分かるでしょう。Symbian OSではこれらの概念を組み合わせて、携帯、スマートホン向けのロバストなプログラムを、効率の良いC++を使って構成することになります。
参考のため、各クラスのインスタンスがどのようにメモリ上に配置されるかを図2に示します。
Symbian OSでは、クラスの作り方のパターンを命名規則(ネーミングコンベンション)で規定しています。ほかの環境では言語でサポートしているGC付きのOOPLとも共通する概念を、Symbian OSでは規約により解決しているわけです。これにより、C++を使いつつ、プログラマーに好き放題想像力を発揮させないセーフな環境が整えられています。
ただし、これだけでメモリリークのないプログラムを作成できるかというと、そんなことはありません。次回はこれらの規約の上に構築されたSymbian OSの三大特徴の1つ、例外処理にも耐えるメモリ管理フレームワークを解説します。(次回に続く)
Copyright © ITmedia, Inc. All Rights Reserved.