Objective-C @protocol在C ++中的等价物

时间:2013-09-11 22:42:36

标签: c++ objective-c delegates

A类有一个B类实例作为成员。有时B类的实例想要与A类交谈。在Objective-C中,我可以这样做:

// A.h
@interface A : NSObject <BDelegate>
@property (nonatomic, retain) B *b;
@end 

// A.m
- (void) classBsays {

}


// B.h
@protocol BDelegate
- (void) classBsays;
@end

@interface B : NSObject
@property (nonatomic, assign) id<BDelegate> delegate;
@end

// B.m
@implementation B
- (void) f {
    [delegate classBsays];
}
@end

我在C ++中使用类B上的void指针做了类似的事情。但是这个部分错过了“B类委托应该实现这样的方法”的部分。

如何在C ++中模仿Objective-C的协议?

2 个答案:

答案 0 :(得分:5)

您的示例的C ++等价物如下所示:

// A.hpp
#include "B.hpp"

class A : public BDelegate {
    public:
        void classBSays ( ) { }
        B* b;
};

// B.hpp
class BDelegate {
    public:
        virtual void classBSays( ) = 0;
};
class B {
    public:
        void f ( ) { delegate->classBSays( ); }
        BDelegate* delegate;
};

请注意,为了简洁起见,我在这里使用了成员函数的内联实现 - 您可以在单独的A.classBSays()B.f()文件中同等地实现A.cppB.cpp如果你想要的话。

在此示例中,类BDelegate是一个抽象基类(ABC),相当于您的BDelegate协议。通过仅包含纯虚拟成员函数(前面带有关键字virtual=0后缀的函数),它强制其子类为这些方法提供实现,就像使用@required标记一样(或没有标签)在Objective-C协议中。 BDelegate仅包含此类函数的事实使其成为ABC。

您可以通过为ABC中的函数指定一个空体来模拟Objective-C @optional标记,这意味着不需要子类来实现它(因为它是在ABC中实现的)。例如,您可以通过修改foo来模拟可选的BDelegate方法,如下所示:

@protocol BDelegate
- (void) classBsays;
@optional
- (void) foo;
@end
// Is approximately equivalent to:
class BDelegate {
    public:
        virtual void classBSays( ) = 0;
        virtual void foo( ) { }
};

使用该定义,类A可以根据需要选择是否提供foo的定义。但请注意,这与Objective-C @optional表示法并不完全相同,因为A如果不提供BDelegate的{​​{1}}方法仍会继承foo覆盖。另一方面,使用Objective-C协议,A根本没有这样的方法,除非它本身明确地实现它。

可以对here进行更全面的主题介绍。

答案 1 :(得分:1)

通过定义抽象基类,您可以在C ++中实现基本相同。也就是说,您只定义方法签名而不提供任何实现。这需要任何子类来实现声明的方法。

以下是您转换为C ++的示例:

// A.h
class A : public BDelegate {
    B *b;
};

// A.m
Result A::classBsays {

}

// B.h
class BDelegate {
    virtual Result classBsays() = 0;
};

class B {
    BDelegate* delegate;
};

// B.m
void B::f {
    delegate->classBsays();
}

请注意,这可能无法编译,因为它缺少一些重要的东西。你需要一个构造函数,传入B引用,甚至可能使用std::shared_ptr作为委托,依此类推。但总体形状是这样的。

重要的部分是BDelegate::classBsays()的方法声明在方法签名之后具有= 0,并且没有实现主体。这意味着该方法是纯虚拟,这意味着子类必须实现该方法,否则无法实例化。 (这是有道理的,否则你可能会调用一个没有实现的方法!)任何具有一个或多个纯虚方法的类本身就是一个纯虚拟类。正如您所描述的那样,这非常常用于定义将系统的不同部分分离的接口。