C多重继承和Objective-C.这是GCC中的错误吗?
我昨天遇到了以下奇怪的行为.这对我来说似乎是一个编译器错误,还是有一些我错过的东西?我使用Objective-C到C适配器类将Facebook Connect for iPhone的Objective-C类包装起来,这样就可以更方便地使用我们自己的OpenGL / C代码.
以下代码显示了该问题.在下面的第一个变体中,编译器编译但是弄乱了vtable,因此调用了错误的方法.在第二个变体中,我们得到一个编译器错误,表明gcc很混乱. 评论试图更详细地解释这种情况. #include <iostream> #import <Foundation/Foundation.h> // An abstract C++ interface class Foo_cpp { public: virtual void foo() = 0; }; // Another abstract C++ interface class Bar_cpp { public: virtual void bar() = 0; }; // An Objective-C to C++ adaptor. // It takes a C++ interface Foo. When it's do_foo method is called it // delegates call to Foo::foo. @interface Foo_objc : NSObject { Foo_cpp* foo_cpp_; } @end @implementation Foo_objc - (id)init:(Foo_cpp*)foo { self = [super init]; if (self) { foo_cpp_ = foo; } return self; } - (void) do_foo { std::cout << "do_foo: "; foo_cpp_->foo(); } @end // Another Objective-C to C++ adaptor. @interface Bar_objc : NSObject{ Bar_cpp* bar_cpp_; } @end @implementation Bar_objc - (id)init:(Bar_cpp*)bar { self = [super init]; if (self) { bar_cpp_ = bar; } return self; } - (void) do_bar { std::cout << "do_bar: "; bar_cpp_->bar(); } @end // Main class implements both abstract C++ interfaces (which will // confuse the compiler as we shall see). // It constructs two Objective-C to C++ adaptors as a members and // tries to pass itself as a C++ delegate for these adaptors. class Main : public Foo_cpp,public Bar_cpp { public: Foo_objc* foo_; Bar_objc* bar_; Main() { // We try to construct two objective-c to c++ adaptors Foo_objc and // Bar_objc. // // We expect output of // [foo_ do_foo]; // [bar_ do_bar]; // to be // do_foo: foo // do_bar: bar #if 0 // This variant compiles but the compiler messes up // the vtables. When do_bar() is called,we expect // bar() to be called via Bar_objc,but instead // foo() is called from both adaptors. // Output is // do_foo: foo // do_bar: foo !!!! Calls wrong method !!!! foo_ = [[Foo_objc alloc] init:this]; bar_ = [[Bar_objc alloc] init:this]; [foo_ do_foo]; [bar_ do_bar]; #else // Now,this variant tries to help the compiler by passing // |this| via a variable of the correct interface type. // It actually reveals the confusion that the compiler // is having. Seems like a bug in the compiler. Foo_cpp* iface = this; foo_ = [[Foo_objc alloc] init:iface]; Bar_cpp* iface2 = this; // Error we get is on the next code line. // $g++ -x objective-c++ -lobjc mheritance_test.mm // mheritance_test.mm: In constructor ‘Main::Main()’: // mheritance_test.mm:107: error: cannot convert ‘Bar_cpp*’ to ‘Foo_cpp*’ in argument passing bar_ = [[Bar_objc alloc] init:iface2]; [foo_ do_foo]; [bar_ do_bar]; #endif } ~Main() { delete foo_; delete bar_; } virtual void foo() { std::cout << "foo" << std::endl; } virtual void bar() { std::cout << "bar" << std::endl; } }; int main() { Main m; } iPhone SDK和Mac自己的版本以及4.0.1和4.2版本会出现此问题. UPDATE #include <iostream> #import <Foundation/Foundation.h> // An abstract C++ interface class Foo_cpp { public: virtual void foo() = 0; }; // Another abstract C++ interface class Bar_cpp { public: virtual void bar() = 0; }; // An Objective-C to C++ adaptor. // It takes a C++ interface Foo. When it's do_foo method is called it // delegates call to Foo::foo. @interface Foo_objc : NSObject { Foo_cpp* foo_cpp_; } @end @implementation Foo_objc - (id)init:(Foo_cpp*)foo { self = [super init]; if (self) { foo_cpp_ = foo; } return self; } - (void) do_foo { std::cout << "do_foo: "; foo_cpp_->foo(); } @end // Another Objective-C to C++ adaptor. @interface Bar_objc : NSObject{ Bar_cpp* bar_cpp_; } @end @implementation Bar_objc - (id)init:(Bar_cpp*)bar { self = [super init]; if (self) { bar_cpp_ = bar; } return self; } - (void) do_bar { std::cout << "do_bar: "; bar_cpp_->bar(); } @end class Main : public Foo_cpp,public Bar_cpp { void foo() { std::cout << "foo" << std::endl; } void bar() { std::cout << "bar" << std::endl; } }; int main() { Main* m = new Main; #if 0 // Compiles but produces // do_foo: foo // do_bar: foo !!! incorrect method called !!! Foo_objc* fo = [[Foo_objc alloc] init:m]; Bar_objc* bo = [[Bar_objc alloc] init:m]; #else // Doesn't compile Foo_objc* fo = [[Foo_objc alloc] init:(Foo_cpp*)m]; Bar_objc* bo = [[Bar_objc alloc] init:(Bar_cpp*)m]; // A line above produces following error // mheritance_test2.mm: In function ‘int main()’: // mheritance_test2.mm:82: error: cannot convert ‘Bar_cpp*’ to ‘Foo_cpp*’ in argument passing #endif [fo do_foo]; [bo do_bar]; } 更新2 解决方法
我正在编辑我的回答提示,因为我完成了任务;-)
我不是一个Objective-C程序员,但是出于好奇心,我不禁想知道发生了什么,并且稍微使用了代码.我发现除了Foo *和Bar *部分并将以下行添加到main()后,问题浮出水面: Bar_objc *bo = [[Bar_objc alloc] init:(Bar_cpp*)0]; 玩了一下之后我发现它必须与未完全定义的alloc消息结果有关.通过将上面的分配分成两部分来解决这个问题: Bar_objc *bo = [Bar_objc alloc]; [bo init:(Bar_cpp*)0]; 这很好用.那么转换alloc结果(参见下面的代码).或者,这可以修复(我相信)具有不同的初始化器名称.也许还重新实现了alloc.不知道. 具有多重继承的完整代码(它还有一些其他的小改动 – 为了简洁,我将类/公共对更改为结构,删除了在构造函数中调用虚拟,将删除调用更改为dealloc消息,可能还有其他内容): #include <iostream> #import <Foundation/Foundation.h> struct Foo_cpp { virtual void foo() = 0; }; struct Bar_cpp { virtual void bar() = 0; }; @interface Foo_objc : NSObject { Foo_cpp* foo_cpp_; } - (id)init:(Foo_cpp*)foo; - (void)do_foo; @end @implementation Foo_objc : NSObject { Foo_cpp* foo_cpp_; } - (id)init:(Foo_cpp*)foo { if( self = [super init] ) foo_cpp_ = foo; return self; } - (void) do_foo { std::cout << "do_foo: "; foo_cpp_->foo(); } @end @interface Bar_objc : NSObject { Bar_cpp* bar_cpp_; } - (id)init:(Bar_cpp*)bar; - (void)do_bar; @end @implementation Bar_objc : NSObject { Bar_cpp* bar_cpp_; } - (id)init:(Bar_cpp*)bar { if( self = [super init] ) bar_cpp_ = bar; return self; } - (void) do_bar { std::cout << "do_bar: "; bar_cpp_->bar(); } @end struct Main : public Foo_cpp,public Bar_cpp { Foo_objc* foo_; Bar_objc* bar_; Main() { foo_ = [(Foo_objc*)[Foo_objc alloc] init:this]; bar_ = [(Bar_objc*)[Bar_objc alloc] init:this]; } ~Main() { [foo_ dealloc]; [bar_ dealloc]; } virtual void foo() { std::cout << "foo" << std::endl; } virtual void bar() { std::cout << "bar" << std::endl; } }; int main() { Main m; [m.foo_ do_foo]; [m.bar_ do_bar]; } 结果: do_foo: foo do_bar: bar 底线:我认为由于键入有点弱,并且无论类型如何都可以向对象发送消息,最好不要使用相同名称但不同参数的消息. (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |