作者: DevBean 日期: 2011 年 03 月 21 日
简单继承
Objective-C 也有继承的概念,但是不能多重继承。不过,它也有别的途径实现类似多重继承的机制,这个我们后面会讲到。
C++ | Objective-C |
|
|
在 C++ 中,一个类可以继承自一个或多个类,使用 public、protected 以及 private 修饰符。子类的函数如果要调用父类的版本,需要使用 :: 运算符,例如 Bar::,Wiz:: 等。
在 Objective-C 中,一个类只能继承一个父类,并且只能是 public 的(这和 Java 是一致的)。同样类似 Java,如果你要在子类中调用父类的函数,需要使用 super。
多重继承
Java 同样不允许多重继承。但是它提供了 interface 来模拟多重继承。类似的,Objective-C 也有同样的机制,这就是协议 protocol 和分类 categories。我们将在后面的内容详细讲述这两种技术。
虚拟性
虚方法
在 Objective-C 中,所有方法都是虚的,因此,没有 virtual 关键字或其等价物。
虚方法重定义
在 Objective-C 中,你可以定义一个没有在 @interface 块里面声明的方法。但这并不是一种替代 private 的机制,因为这种方法实际是能够被调用的(回想下,Objective-C 中方法的调用是在运行期决定的)。不过,这确实能够把接口定义变得稍微干净了一些。
这并不是一种坏习惯,因为有时你不得不重定义父类的函数。由于所有方法都是虚的,你无需像 C++ 一样在声明中显式写明哪些函数是 virtual 的,这种做法就成为一种隐式的重定义。很多继承西 NSObject 的方法都是是用这种方法重定义的。例如构造方法 init,析构方法 dealloc,view 类的 drawRect: 等等。这样的话,接口就变得更简洁,更易于阅读。不好之处就是,你不能知道究竟哪些方法被重定义了。
纯虚方法则是使用正式协议 formal protocols 来实现。
虚继承
Objective-C 中不允许多重继承,因此也就没有虚继承的问题。
协议
Java 和 C# 使用接口 interface 的概念来弥补多重继承的不足。Objective-C 也使用了类似的机制,成为协议 protocol。在 C++ 中,这种概念是使用抽象类。协议并不是真正的类:它只能声明方法,不能添加数据。有两种类型的协议:正式的 formal 和非正式的 informal。
正式协议
正式协议的方法,所有实现这个协议的类都必须实现。这就是一种验证,也就是说,只要这个类说实现这个协议,那么它肯定可以处理协议中规定的方法。一个类可以实现任意多个协议。
C++
class MouseListener
{
public:
virtual bool mousePressed(void) = 0; // 纯虚方法
virtual bool mouseClicked(void) = 0; // 纯虚方法
};
class KeyboardListener
{
public:
virtual bool keyPressed(void) = 0; // 纯虚方法
};
class Foo : public MouseListener, public KeyboardListener {...}
// Foo 必须实现 mousePressed, mouseClicked 和 keyPressed
// 然后 Foo 就可以作为鼠标和键盘的事件监听器
Objective-C
@protocol MouseListener
-(BOOL) mousePressed;
-(BOOL) mouseClicked;
@end
@protocol KeyboardListener
-(BOOL) keyPressed;
@end
@interface Foo : NSObject <MouseListener, KeyboardListener>
{
...
}
@end
// Foo 必须实现 mousePressed, mouseClicked 和 keyPressed
// 然后 Foo 就可以作为鼠标和键盘的事件监听器
C++ 中,协议可以由抽象类和纯虚函数实现。C++ 的抽象类要比 Objective-C 的协议强大的多,因为抽象类可以带有数据。
Objective-C 中,协议是一个特殊的概念,使用尖括号 <…> 表明。注意,尖括号在 Objective-C 中不是模板的意思,Objective-C 中没有类似 C++ 模板的概念。
一个类也可以不经过协议声明,直接实现协议规定的方法。此时,conformsToProtocol: 方法依然返回 NO。出于性能考虑,conformsToProtocol: 方法只检查类接口的声明,不会一个方法一个方法的对比着检查。conformsToProtocol: 的返回值并不会作为是否调用方法的依据。下面是这个方法的原型:
-(BOOL) conformsToProtocol:(Protocol*)protocol
// Protocol 对象可以由 @protocol(协议名) 返回
实现了正式协议的对象的类型同协议本身是兼容的。这一机制可以作为协议的筛选操作。例如:
// 下面方法是 Cocoa 提供的标准方法
// 方法参数可以是任意类型 id,但是必须兼容 NSDraggingInfo 协议
-(NSDragOperation) draggingEntered:(id )sender;
可选方法
有时我们需要这么一种机制:我们的类需要实现一部分协议中规定的方法,而不是整个协议。例如在 Cocoa 中,代理的概念被广泛使用:一个类可以给定一个辅助类,由这个辅助类去完成部分任务。
一种实现是将一个协议分割成很多小的协议,然后这个类去实现一个协议的集合。不过这并不具有可操作性。更好的解决方案是使用非正式协议。在 Objective-C 1.0 中就有非正式协议了,Objective-C 2.0 则提出了新的关键字 @optional 和 @required,用以区分可选方法和必须方法。
@protocol Slave
@required // 必须部分
-(void) makeCoffee;
-(void) duplicateDocument:(Document*)document count:(int)count;
@optional // 可选部分
-(void) sweep;
@required // 又是一个必须部分
-(void) bringCoffee;
@end
非正式协议
非正式协议并不是真正的协议,它对代码没有约束力。非正式协议允许开发者将一些方法进行归类,从而可以更好的组织代码。所以,非正式协议并不是协议的宽松版本。另外一个相似的概念就是分类。
让我们想象一个文档管理的服务。假设有绿色、蓝色和红色三种文档,一个类只能处理蓝色文档,而 Slave 类使用三个协议 manageBlueDocuments, manageGreenDocuments 和 manageRedDocuments。Slave 可以加入一个分类 DocumentsManaging,用来声明它能够完成的任务。分类名在小括号中被指定:
@interface Slave (DocumentsManaging)
-(void) manageBlueDocuments:(BlueDocument*)document;
-(void) trashBlueDocuments:(BlueDocument*)document;
@end
任何类都可以加入 DocumentsManaging 分类,加入相关的处理方法:
@interface PremiumSlave (DocumentsManaging)
-(void) manageBlueDocuments:(BlueDocument*)document;
-(void) manageRedDocuments:(RedDocument*)document;
@end
另一个开发者就可以浏览源代码,找到了 DocumentsManaging 分类。如果他觉得这个分类中有些方法可能对自己,就会检查究竟哪些能够使用。即便他不查看源代码,也可以在运行时指定:
if([mySlave respondsToSelector:@selector(manageBlueDocuments:)])
[mySlave manageBlueDocuments:document];
严格说来,除了原型部分,非正式协议对编译器没有什么意义,因为它并不能约束代码。不过,非正式协议可以形成很好的自解释性代码,让 API 更具可读性。
本文来自 DevBean's World:http://www.devbean.info。
转载时请标明文章原始出处:http://www.devbean.info/2011/03/from_cpp_to_objc_7/。