Objective-C から C++ の仮想関数を呼ぶ
仮想メンバ関数がある場合、当該C++クラスはObjective-Cのインスタンス変数として機能しません。
#import <Cocoa/Cocoa.h> struct Class0 { void foo(); }; struct Class1 { virtual void foo(); }; struct Class2 { Class2(int i, int j); }; @interface Foo :NSObject { Class0 class0; // OK Class1 class1; // エラー! Class1 *ptr; // OK—Fooのinitから'ptr = new Class1()'を呼び出し、 // Fooのdeallocから'delete ptr'を呼び出す Class2 class2; // 警告—コンストラクタを呼び出さない! ... @endC++では、仮想関数を含むクラスの各インスタンスが、適切な仮想関数テーブルポインタを含む必要があります。しかし、Objective-CランタイムはC++オブジェクトモデルを知らないため、仮想関数テーブルポインタを初期化することができません。同様に、Objective-Cランタイムは、それらのオブジェクトのC++コンストラクタまたはデストラクタに対して呼び出しを発行できません。C++クラスにユーザ定義コのンストラクタやデストラクタがあっても、それらは呼び出されません。そのような場合、コンパイラは警告を発します。
日本語ドキュメント - Apple Developer
とあるのだが、C++オブジェクトをポインタで持つなら問題ないの?仮想関数を呼んでも大丈夫なの?というのがよくわからないのでテスト。
#import <Foundation/NSObject.h> #import <stdio.h> // C++ 親クラス class Parent { public: virtual void sayHello() { printf("hello, I'm C++ parent.\n"); } virtual ~Parent() { printf("parent destructor\n"); } }; // C++ 子クラス class Child : public Parent { public: virtual void sayHello() { printf("hello, I'm C++ child.\n"); } virtual ~Child() { printf("child destructor\n"); } }; // Objective-C クラス @interface ObjC : NSObject { Parent *cppObject; // 親クラスのポインタとして保持 } - (void)sayHello; @end @implementation ObjC - (id)init { if (self = [super init]) { cppObject = new Child(); // 子クラスのオブジェクトを作成 } return self; } - (void)dealloc { delete cppObject; [super dealloc]; } - (void)sayHello { cppObject->sayHello(); } @end int main(int argc, char *argv[]) { id objc = [[ObjC alloc] init]; [objc sayHello]; [objc release]; }
これを実行。
$ gcc -o main main.mm -framework Foundation -lstdc++ $ ./main hello, I'm C++ child. child destructor parent destructor
期待通り動いてる。
解釈すると、C++オブジェクトの動作は 'Objective-Cランタイム' の外で行われている。Objectice-Cランタイムに暗黙的なC++オブジェクトの生成と破棄をさせなければ、つまりC++オブジェクトを全てポインタで持つのであれば、仮想関数があっても問題はない。ということだと思います。
'Call C++ Default Ctors/Dtors in Objective-C' を on にした時の挙動は調べないとわからない。