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的协议?
答案 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.cpp
和B.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
,并且没有实现主体。这意味着该方法是纯虚拟,这意味着子类必须实现该方法,否则无法实例化。 (这是有道理的,否则你可能会调用一个没有实现的方法!)任何具有一个或多个纯虚方法的类本身就是一个纯虚拟类。正如您所描述的那样,这非常常用于定义将系统的不同部分分离的接口。