C++中等价于Objective-C @protocol的是什么?

5

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
一个只有纯虚函数成员的C++类(参见此处了解基本介绍)大致相当于Objective-C协议。 - Mac
2个回答

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 后缀的函数),强制其子类提供这些方法的实现,就像在 Objective-C 协议中使用 @required 标签(或不使用标签)一样。 BDelegate 仅包含这些函数是使它成为 ABC 的原因。
您可以通过在 ABC 中指定一个空体来模拟 Objective-C 的 @optional 标签,这意味着子类不需要实现它(因为它已经在 ABC 中实现)。例如,您可以通过修改 BDelegate 来模拟可选的 foo 方法:
@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没有提供自己的覆盖,则仍将继承BDelegatefoo方法。另一方面,使用Objective-C协议,除非明确实现,否则A根本没有这样的方法。

有关此主题的更详细介绍,请单击此处


2

您可以通过定义抽象基类来实现与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在方法签名后面,并且没有实现体。这意味着该方法是纯虚方法,这意味着子类必须实现该方法,否则它将无法实例化。(这是有道理的,否则你可能会调用一个没有实现的方法!)任何具有一个或多个纯虚方法的类本身都是一个纯虚类。这经常被用来定义接口,以解耦系统的不同部分。

1
协议还可以定义可选方法,这些方法将由带有 {} 实现的虚拟方法处理,而不是声明为纯虚拟。 - Anya Shenanigans
1
@Petesh:这可能是最接近的等价物,但并不完全相同。特别是,如果C++的BDelegate类包含一个virtual foo(){}函数,则可以保证A具有这样的成员函数。另一方面,如果Objective-C的BDelegate协议包含一个@optional -(void) foo;,那么A可能有也可能没有foo方法(完全取决于它是否在A中显式实现)。 - Mac
调用者可以确定可选方法是否已实现,并相应地更改其行为,以一种无法通过基类中的默认实现实现的方式。 - gnasher729

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接