Класс 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 ++, используя указатель void на класс B. Но здесь пропущена часть, в которой говорится, что «делегат класса B должен реализовывать такие-то и такие-то методы».
Как я могу имитировать протокол Objective-C в C ++?
Эквивалент 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
содержит только такие функции, что делает его азбукой.
Вы можете подражать Objective-C @optional
пометить, указав пустое тело для функции в вашем ABC, что означает, что подклассы не требуются для его реализации (так как это реализовано в 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
«s foo
метод, если он не обеспечивает свое собственное переопределение. С протоколом Objective-C, с другой стороны, A
не будет иметь такой метод вообще, если он явно не реализует его сам.
Более подробное введение в предмет доступно Вот.
Вы можете достичь в основном того же в 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
после подписи метода, и не имеет тела реализации. Это означает, что метод чисто виртуальный, что означает подкласс должен реализовать метод, или его нельзя реализовать. (Это имеет смысл, в противном случае вы могли бы потенциально вызвать метод, который не имеет реализации!). Любой класс, имеющий один или несколько чистых виртуальных методов, сам является чистым виртуальным классом. Это очень часто используется так же, как вы описываете, чтобы определить интерфейсы, которые разъединяют различные части системы.