【问题标题】:Objective-C @protocol equivalent in C++C++ 中的 Objective-C @protocol 等效项
【发布时间】:2013-09-11 22:42:36
【问题描述】:

类 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 的协议?

【问题讨论】:

  • 只有纯虚成员函数的 C++ 类(基本介绍请参见 this)大致相当于 Objective-C 协议。

标签: c++ objective-c delegates


【解决方案1】:

您的示例的 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.cppB.cpp 文件中同样实现 A.classBSays()B.f()

在本例中,BDelegate 类是一个抽象基类 (ABC),相当于您的 BDelegate 协议。通过只包含纯虚成员函数(以关键字virtual=0 后缀开头的函数),它强制其子类为这些方法提供实现,就像使用@required 标签(或无标签)一样在 Objective-C 协议中。 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 根本没有这样的方法,除非它自己显式实现它。

更详尽的主题介绍请见here

【讨论】:

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

    【讨论】:

    • 协议还可以定义可选方法,这些方法将由具有 {} 实现的虚拟方法提供服务,而不是被声明为纯虚拟
    • @Petesh:这可能是最接近的等价物,但并不完全相同。特别是,如果 C++ BDelegate 类包含 virtual foo(){} 函数,那么可以保证 A 具有这样的成员函数。另一方面,如果 Objective-C 的BDelegate 协议包含@optional -(void) foo;,那么A 可能有也可能没有foo 方法(完全取决于它是否在A 中显式实现) .
    • ... 调用者可以确定是否实现了可选方法这一事实,并相应地以某些基类中的默认实现无法实现的方式更改其行为。
    猜你喜欢
    • 1970-01-01
    • 2012-11-26
    • 2013-02-09
    • 2012-04-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多