以前Objective-CとC++のハイブリッド・オブジェクトについて紹介したが、今回もObjective-C++の面白い応用について紹介する。
Objective-CはC言語を拡張してオブジェクト指向をサポートした言語で、同じくC言語を拡張してオブジェクト指向をサポートしたC++とは、いろいろ比較されることも多い。特にObjective-Cは、Objective-Cに出来てC++に出来ないことよりも、C++に出来てObjective-Cに出来ないことの方が多いため、低く見られがちだ。その一つに、Objective-Cでは演算子オーバーロードができないことが上げられる。
しかし、Objective-C++を使うと、Objective-Cも演算子オーバーロードの恩恵を受けられるようになる。それも、以外と簡単な方法で。
まず最初に、C++でid型のラッパークラスを定義する。
class Id {
id object;
public:
// 変換コンストラクタ
Id(id obj) : object(obj) {}
// =演算子オーバーロード
void operator=(id obj) { object = obj; }
// 変換演算子
operator id() const { return object; }
};
これは、C++のオブジェクトでありながら、id型の変数とそっくりな動作をする。まず、変換コンストラクタと=演算子オーバーロードにより、キャストなしでObjective-Cのオブジェクトを代入できる。さらに、変換演算子により、キャストなしでObjective-Cのメソッド呼び出しができてしまう。
// 変換コンストラクタ
Id str1 = @"hello, ";
// =演算子オーバーロード
Id str2;
str2 = @"world.";
// 変換演算子
char* s = [str1 UTF8String];
これで準備は完了。あとは、このIdクラスを利用して演算子オーバーロードを定義していくだけだ。たとえば、+演算子オーバーロードは次のように定義できる。
@implementation NSString (Operator)
- (id)op_add:(id)obj {
return [self stringByAppendingString:obj];
}
@end
Id operator+(Id obj1, Id obj2) {
return [obj1 op_add:obj2];
}
C++側で、+演算子の処理をObjective-Cのop_add:メソッド呼び出しに変換。あとは、Objective-Cの任意のクラスにop_add:メソッドを実装していくだけだ。
Id str1 = @"hello, ";
Id str2 = str1 + @"world.";
printf("message: %s", [str2 UTF8String]);
// 出力: message: hello, world.
これで念願の+演算子による文字列結合が実現する。見た目もスマートだ。
このid型ラッパークラスと演算子オーバーロードは、他にも様々な応用が可能だ。たとえば、double型の変換演算子を定義すると、
class Id {
...
operator double() const {
return [object doubleValue];
}
};
Id str = @"3";
int num = str + 4;
printf("%d", num);
// 出力: 7
文字列と整数の足し算ができる。まるで、どこかのスクリプト言語のようだ。
また、=演算子をこのように定義すれば、
class Id {
...
void operator=(id obj) {
if(object == obj) return;
[object release];
object = [obj retain];
}
};
ガベージコレクションがなくても、retainとreleaseの記述を省略できる。
デストラクタを定義すれば、
class Id {
...
~Id() { [object autorelease]; }
};
- (id)get {
Id obj = [MyObject new];
return obj;
}
メソッドから返る時に、autoreleaseの記述を省略できる。
この他にも、Java1.5のようにNSNumberとintとのオートボクシングなど、考えられる応用技はまだまだあるだろう。
Objective-C++は、Objective-Cに新しい世界を開いてくれる。