如果您已经了解 Objective-C,如何学习 C++
C++ 和 Objective-C 有何区别?
两者都完全支持面向对象范式。两者都不是绝对和普遍地 “比对方更好”。但它们之间存在差异。最重要的差异在于:
注意: 如果你有 Objective-C 背景,本节将告诉你有效使用 C++ 所需的最重要事项。请不要认为任何一种语言是某种程度上 “劣等”或“糟糕”,也不要认为本节是在推广某种语言而贬低另一种。我不是语言偏执狂,曾任 ANSI C++ 和 ANSI Smalltalk 标准化委员会 成员,并且 我不提倡语言偏执。相反,本节旨在帮助你理解(并接受!)这些差异。
什么是“静态类型”,它与 Objective-C 有何异同?
静态类型意味着编译器在静态(编译时)检查每个操作的类型安全性,而不是生成在运行时检查的代码。例如,使用静态类型时,函数参数的签名匹配是在编译时检查的,而不是在运行时。不正确的匹配会被编译器标记为错误,而不是由运行时系统标记。
在面向对象代码中,最常见的“类型不匹配”是对不准备处理该操作的对象调用成员函数。例如,如果 class
Fred
有成员函数 f()
而没有 g()
,并且 fred
是 class
Fred
的一个实例,那么 fred.f()
是合法的,而 fred.g()
是非法的。C++(静态类型)在编译时捕获错误,而 Objective-C(动态类型)的 @dynamic part@
在运行时捕获错误。(严格来说,C++ 像 Pascal —伪静态类型— 因为指针转换和联合可以用来违反类型系统;这提醒我:使用 指针转换 和联合的频率应与使用 goto
的频率相同)。
“静态类型”还是“动态类型”更适合 C++?
[欲了解背景信息,请阅读 上一篇常见问题]。
如果你想最有效地使用 C++,请将其作为一种静态类型语言来使用。
C++ 的灵活性足以让你(通过指针转换、联合和 #define
宏)使其“看起来”像 Objective-C。但请不要这样做。这提醒我:尽量避免使用 #define
:它在 4 个方面是邪恶的:邪恶#1、邪恶#2、邪恶#3 和 邪恶#4。
有些地方指针转换和联合是必要甚至有益的,但它们应该谨慎和少量使用。指针转换是告诉编译器相信你。不正确的指针转换可能会破坏你的堆,侵占其他对象拥有的内存,调用不存在的成员函数,并导致普遍的故障。这可不是什么好看的景象。如果你避免使用这些和相关构造,你的 C++ 代码可以更安全更快,因为任何可以在编译时检查的东西都无需在运行时完成。
如果你有兴趣使用指针转换,请使用新式的指针转换。最常见的例子是将旧式指针转换(例如 (X*)p
)改为新式动态转换(例如 dynamic_cast<X*>(p)
),其中 p
是一个指针,X
是一个类型。除了 dynamic_cast
之外,还有 static_cast
和 const_cast
,但 dynamic_cast
是模拟动态类型大部分优势的那个(另一个是 typeid()
构造;例如,typeid(*p).name()
将返回 *p
的类型名称)。
C++ 中如何使用继承,这与 Objective-C 有何不同?
有些人认为继承的目的是代码复用。在 C++ 中,这是错误的。简单地说,“继承不是为了代码复用。”
C++ 中继承的目的是表达接口兼容性(子类型化),而不是为了代码复用。在 C++ 中,代码复用通常通过组合而非继承来实现。换句话说,继承主要是一种规范技术,而不是一种实现技术。
这是与 Objective-C 的主要区别,后者只有一种形式的继承(C++ 提供 private
继承表示“共享代码但不符合接口”,而 public
继承表示“某种程度上符合”)。Objective-C 语言本身(与编码实践相对)允许你通过提供一个调用“不理解”方法的重写来达到“隐藏”继承方法的效果。此外,Objective-C 允许概念上的“is-a”关系独立于继承层次结构而存在(子类型不必是派生类;例如,你可以使某个东西是 Stack
但不从 class
Stack
继承)。
相比之下,C++ 对继承的限制更多:如果没有使用继承,就没有办法建立“概念上的 is-a”关系(C++ 的变通方法是通过 ABC 将接口与实现分离)。C++ 编译器利用与公共继承相关的额外语义信息来提供静态类型。
Objective-C/C++ 继承差异的实际后果是什么?
[欲了解背景信息,请阅读 上一篇常见问题]。
Objective-C 允许你创建不是派生类的子类型,也允许你创建不是子类型的派生类。这使得 Objective-C 程序员可以非常随意地将数据(位、表示、数据结构)放入类中(例如,你可能会将一个链表放入 class
Stack
中)。毕竟,如果有人想要一个基于数组的 Stack
,他们不必从 Stack
继承;如果需要,他们可以从 Array
继承这样的类,即使 ArrayBasedStack
不是一种 Array
!
在 C++ 中,你不能如此随意。只有机制(成员函数代码)可以被派生类重写,而表示(数据位)不能。因此,你通常最好不要将数据结构放入类中。这导致对 抽象基类 的更强依赖。
我喜欢将这种差异想象成全地形车(ATV)和 玛莎拉蒂 之间的区别。全地形车更有趣,因为你可以在田野、溪流、人行道等地“玩耍”。而玛莎拉蒂则能让你更快到达目的地,但它强制你留在路上。我对 C++ 程序员的建议很简单:走在正道上。即使你是一个喜欢“表达自由”穿越灌木丛的人,也不要在 C++ 中这样做;这不合适。