Objective-Cは、C言語にマクロによってオブジェクト指向の機能を追加した言語である。Objective-Cのソースコードは、マクロ展開されてC言語のソースコードになったあと、普通にCコンパイラによってコンパイルするというのが基本原理だ。
そして、このとき、Cコンパイラの代わりにC++コンパイラでコンパイルするものを、Objective-C++と呼んでいる。
Objective-C++によって、1つのソースコード内に2つ言語を混在させることができるようになる。Objective-CのインスタンスもC++のインスタンスも、ただのポインタとして扱えば相互に利用し合う事が可能だ。
しかし、C++コンパイラを使うことによって、Objective-Cのマクロな部分がパワーアップする訳ではないので、ハイブリッドな機能をもったクラスや、ハイブリッドなインスタンスを作ることはできない。あくまで、Objective-C言語 & C++言語なのである。
と、いうのが通説だったが、ハイブリッドな機能をもったクラスや、ハイブリッドなインスタンスは、ある程度までなら実現可能なようだ。それも今まで気づかなかったのが不思議なくらい簡単な方法で。
@interface Hyblid : NSObject {
int number;
}
- (void)setValue:(int)value;
class Hyblid_ {
@defs(Hyblid)
public:
int getValue();
};
@end
@implementation Hyblid
- (void)setValue:(int)value {
number = value;
}
int Hyblid_::getValue() {
return number;
}
@endまず、ハイブリッドなクラスのインスタンス変数は、全てObjective-C側で定義する。そして、Objective-Cのメソッド定義の中に、C++のメンバ関数定義を混在させるような形でクラスを作って行く。このときC++のクラス定義のなかに@defs(Hyblid)と書くのがポイントだ。
こうすると、Objective-CのメソッドとC++メンバ関数の双方で、同じインスタンス変数が参照できるようになる。
Hyblid* obj = [[Hyblid alloc] init];
[obj setValue:123];
NSLog(@"value: %i", ((Hyblid_*)obj)->getValue());
// [出力] value: 123allocで生成したインスタンスは、型をキャストさえすれば、Objective-CとC++のどちらでもアクセスできる。まさにハイブリッド。
このような事が可能なのは、ようするに、これがC++版のToll-Free Bridgeだからだ。
そういう訳なので、例えば、C++のコンストラクタで、
Hyblid_() {
isa = NSClassFromString(@"Hyblid");
[(id)this init];
}このように実装すると、newでもインスタンスが生成できるようになる。ただし、インスタンス変数が0で初期化されないなど、多少動作は異なる。
Hyblid_* obj = new Hyblid_();
[(id)obj setValue:123];
NSLog(@"value: %i", obj->getValue());
// [出力] value: 123また、面白いことに、newで生成したインスタンスも、retain countが有効なようで、C++のインスタンスでありながら、Objective-Cと同じ方法でインスタンスの破棄ができる。deleteを使う必要はない。
Hyblid_* obj = new Hyblid_();
NSLog(@"%i", [(id)obj retainCount]); // [出力] 1
[(id)obj retain];
NSLog(@"%i", [(id)obj retainCount]); // [出力] 2
[(id)obj release];
NSLog(@"%i", [(id)obj retainCount]); // [出力] 1
[(id)obj release];
NSLog(@"%i", [(id)obj retainCount]); // エラー!でも、これで何もかもうまく行くわけではない、ADCのドキュメントにあるように、制約事項は多い。まず、インスタンス生成時の初期化や破棄時の後始末の仕組みが、Objective-CとC++で違う。allocやrelease使ったときは、C++のコンストラクタやデストラクタは呼ばれない。逆に、newやdeleteを使ったときは、Objective-Cのinitやdeallocは呼ばれない。それから、ハイブリッドなクラスから、ハイブリッドなサブクラスを作ることも、ちょっと難しいだろう。
Cocoaは、Objective-Cが持つ動的な特性を最大限に活用したフレームワークであるが、実際のところ、そういった動的なものが必要とされるのは、プログラム全体から見れば、ほんの一部でしかない。プログラムの大部分は、静的な言語のニーズの方が強いのだ。つまり、静的型付を基本とし、ピンポイントで動的型付が使える言語が理想的ということになる。Objective-C++は、そのようなニーズに対応できるのかもしれない。