【转】向iOS开发者介绍C++(-)

原文链接 :http://blog.csdn.net/zhiganglet/article/details/24475931

你已经精通了Objective-C,并且一直想学更酷的东西?看看这篇文章吧!本文将向iOS开发者介绍C++。稍后我会介绍,Objective-C能够无缝地使用C和C++代码。因此,基于以下几点原因,iOS开发者理解C++将会很有帮助:

1.有时候你想在应用中使用一个用C++编写的库。

2.你可能用C++写一部分应用程序的代码,以便更容易跨平台移植。

3. 了解其他语言通常能帮助你更好地理解编程。

这篇文章针对那些已经理解Objective-C的iOS开发者。前提是假定你已明白怎么写Objective-C代码,并熟悉基本的C概念,比如类型、指针、函数等。

准备好学C++了么?那么就马上开始吧!

开始:语言简史

C++和Objective-C有一些共源:它们都根植于老式的好用的C语言,都是C语言的“超集”。因此,你可以在这两种语言中使用C语言的一些功能,和每种语言的附加特性。

如果你熟悉Objective-C,那么你将能粗略地理解你所遇到的C++代码。例如,两种语言中的数值类型(int型、float型和char型)的表现方式和使用规则都是完全一样的。

Objective-C和C++都在C语言基础上添加了面向对象的特征。如果你不熟悉“面向对象”,那么你真正需要明白的是面向对象指数据是由对象表示的,而对象是类的实例。事实上,C++最初称为“C with Classes”,内在的涵义是使C++面向对象。

“那么有什么区别么?”我听到了你的疑问。最大的区别是面向对象特性的方法。在C++中,很多行为是发生在编译时,而在Objective-C中,大多数是发生在运行时。你可能已经修改了Objective-C的运行时间来实现了一个类似method swizzling的诡计,而在C++中这是不可能的。

C++也不像Objective-C一样有大量内省以及映射方法。在C++中,没有办法获得C++对象的类,而在Objective-C中你可以在一个实例中调用“类”方法。同样的,在C++中也没有相当于isMemberOfClass或者isKindOfClass的类。

以上对C++的粗略介绍显示了C++和Objective-C的历史和主要不同点。历史部分已经完成了,到我们继续学习一些C++特征的时间了。

C++ 类

在任何面向对象语言中,首先你要知道的是如何定义一个类。在Objective-C中,你通过创建一个头文件和一个执行文件来定义一个类,在C++中同样如此,语法也十分相似。

如下,是一个Objective-C类的例子:

  1. // MyClass.h
  2. #import <Foundation/Foundation.h>
  3. @interface MyClass : NSObject
  4. @end
  5. // MyClass.m
  6. #import “MyClass.h”
  7. @implementation MyClass
  8. @end

作为一个经验丰富的iOS开发者你应该很明白,但是看看同样用C++写的例子:

  1. // MyClass.h
  2. class MyClass {
  3. };
  4. // MyClass.cpp
  5. #include “MyClass.h”
  6. /* Nothing else in here */

这里有一些本质的区别。首先,C++中的实现文件中什么都没有,这是因为你并没有在类中声明任何的方法。同理,就像Objective-C,一个空类不需要@implemenation/@end模块。

在Objective-C中,几乎每个类都继承自NSObject。你可以创建自己的根类,这意味着你的类将没有任何superclass。但是,你可能从来没有这么做过,除非你只是为了运行时好玩儿。对比C++,正如上面的例子一样,创建一个没有超类的类是很普遍的。

另外一个微小的区别是#include和#import。Objective-C将#import预处理器指令添加到C。在C++中没有相同的,标准的C-style是使用#include。Objective-C中的#import是确保一个文件只被包含一次,但在C++中你必须自己检查。

类成员变量和成员函数

当然,有比声明一个类多得多的事情。正如,在Objective-C和C++中,你可以在类中添加实例变量和方法。或许,你知道在C++中这两个不是这样命名的,C++中通常称为成员变量和成员函数。

 注意:“method(实体方法)”这个术语通常不用于C++中,这个特性只用在Objective-C中。在Objective-C中,通过消息分派带调用“method(实体方法)”。另外,function(函数)通过一个静态的C-style函数被调用。稍后在这篇文章中我将更多的解释静态和动态。

那么接下来你要如何声明成员变量和成员函数呢?如下:

  1. class MyClass {
  2. int x;
  3. int y;
  4. float z;
  5. void foo();
  6. void bar();
  7. };

这里有三个成员变量和两个成员函数。但是在C++中这里要有更多,在C++中,你可以限定成员变量和成员函数的范围,并且可以声明它们是公开访问的还是私有访问的。这个可以用于限制什么代码可以访问每个变量或者函数。

思考下面这个例子:

  1. class MyClass {
  2. public:
  3. int x;
  4. int y;
  5. void foo();
  6. private:
  7. float z;
  8. void bar();
  9. }

这里,x,y和foo函数是公开访问。意思是可以在MyClass类的外部被调用。然而,z和bar函数是私有的。意味着只能在MyClass内部调用被调用。成员变量默认是私有的。

虽然这种区别确实存在于Objective-C中的实例变量中,但是很少使用。另外,在Objective-C中不太可能限制方法的调用范围。即使你只是在实现类内部声明一个方法而没有在接口中显示,技术上你还是可以外部调用这个方法。

Objective-C中的方法只约定为公开或私有。这就是为什么很多开发者选择给私有方法加前缀(例如“p_”前缀)来定义这个区别。这是为了和C++作比较,在C++中如果你试图从类的外部调用一个私有方法,编译器会抛出一个错误。

那么你要怎么使用类呢?和Objective-C非常相似,真的!你可以像下面这样创建一个实例:

  1. MyClass m;
  2. m.x = 10;
  3. m.y = 20;
  4. m.foo();

简单吧!这里创建了一个MyClass的实例,分别设x=10,y=20,然后调用foo函数。

实现类的成员函数

你已经看到了如何定义一个类接口,但是函数呢?事实证明,这个十分简单。有如下两种方法你可以定义。

第一个实现函数的方法是在类的实现文件中定义--.cpp文件。例如:

  1. // MyClass.h
  2. class MyClass {
  3. int x;
  4. int y;
  5. void foo();
  6. };
  7. // MyClass.cpp
  8. #include “MyClass.h”
  9. MyClass::foo() {
  10. // Do something
  11. }

以上是第一个方法。在Objective-C中定义十分简单。注意MyClass::的用法,这就是你如何表明foo()函数已经作为MyClass类的一部分被实现了。

第二个实现函数的方法是你在Objective-C中不能做到的。在C++中,你可以直接在头文件中定义一个函数,如下:

  1. // MyClass.h
  2. class MyClass {
  3. int x;
  4. int y;
  5. void foo() {
  6. // Do something
  7. }
  8. };

如果你只用过Objective-C,这看上去会很奇怪。确实奇怪,但是这种方法会十分有用。当一个函数以这种方式被声明时,编译器可以执行“内联”优化。这意味着当函数被调用时,整个函数代码在调用站点被内联编译而不是跳到一个新的代码块。

虽然内联可以使代码更快,但会增加编辑器代码的大小,因为如果函数被多次调用,代码将通过二进制复制。如果函数很大,或者被调用很多次,那么这可能会对二进制文件的大小产生重大的影响。由于很少的代码会在缓存中,这将会导致性能下降,这就意味着可能会有潜在的更多的缓存丢失。

我的目标是举例证明C++允许更多的灵活性。作为一个开发者,你需要去理解权衡并做决定。当然,唯一能真正明白哪种选择对你是正确的方法就是测试你的代码!

命名空间

上面的例子介绍了一些你之前没有遇到过的新的语法--双冒号::,即指在C++中如何指代范围。双冒号用来告诉编译器应该在哪里可以找到foo函数。

下一次你会在使用命名空间的时候遇到双冒号。命名空间是分离代码的一种方式,以便减少命名冲突。

例如,你可能会在代码中定义一个叫Person的类,但是一个第三方库也可能命名一个叫Person的类。因此,在写C++代码时,你通常会将你的代码放到一个命名空间中来避免这些类型的命名冲突。

很容易做这个,套用以下命名空间声明即可:

  1. namespace MyNamespace {
  2. class Person { … };
  3. }
  4. namespace LibraryNamespace {
  5. class Person { … };
  6. }

现在,当使用任何一个Person类的实现时,你可以使用两个冒号消除歧义,如下:

  1. MyNamespace::Person pOne;
  2. LibraryNamespace::Person pTwo;

简单吧?

除了在类前加一个前缀来约定,在Objective-C中没有类似的命名空间。你确实这样命名类,对吧?如果不是这样命名的话,那就马上这样做吧!

 注意:在Objective-C中已经有很多命名空间的建议了。这样的方案可以在这里(链接)找到。我不知道在Objective-C中是否还能用到它们,但是我希望如此。

内存管理

哦,不……不是那个可怕的词吧!在任何语言中,内存管理都是需要理解的最重要的概念之一。Java基本上是用内存回收器来管理内存。Objective-C需要你明白引用计数以及ARC所扮演的角色。在C++中,嗯。。。C++又不同了。

首先,在C++中,要理解内存管理,你需要先了解堆和栈。即使你认为你知道这一点,我建议你继续往下阅读,或许你能略有收获。

是指用于运行应用程序的一个内存块。栈大小固定,并用于存储应用程序的代码的数据。栈基于puch/pop工作,当一个给定函数将数据压入栈中,当函数运行结束时,出栈的必须是等量的数据。因此,随着时间的推移,栈使用率不会增长。

同样也是运行应用程序的一个内存块。堆大小不固定,并且随着程序的运行而增长。应用程序倾向于使用堆来储存在函数范围外使用的数据。此外,大的数据单元通常会存储到堆中,因为存到栈中有可能会溢出。--记住,栈的大小是固定的。

以上是一个堆和栈原理的简述,以下为两者的C语言示例:

  1. int stackInt = 5;
  2. int *heapInt = malloc(sizeof(int));
  3. *heapInt = 5;
  4. free(heapInt);

这里,stackInt使用栈空间。程序返回后,用来存储“5”的这块内存就会自动释放。

然而,heapInt使用堆空间,在堆上调用malloc分配足够的空间来存储一个整数(int)。但是由于堆必须是由你分配,在用完数据后,开发者需要调用一个free函数来确保你没有内存泄露。

在Objective-C中,你只能在堆上创建对象。如果你试着在栈上创建对象,那么编译器就会报错。根本行不通。

思考下面的例子:

  1. NSString stackString;
  2. // Untitled 32.m:5:18: error: interface type cannot be statically allocated
  3. //         NSString stackString;
  4. //                  ^
  5. //                  *
  6. // 1 error generated.

这就是为什么在Objective-C代码上会看到星号,所有的对象都在堆上创建,并且所有对象都有指针。这在很大程度上归结为Objective-C处理内存管理。引用计数广泛应用于Objective-C中,对象需要在堆中以便它们的生命周期能被严格控制。

在C++中你既可以把数据存到栈中也可存到堆中。由开发者自己决定。然而,在C++中你也必须自己管理内存。数据放入栈中时内存将自动被处理;但用堆时,你必须自己管理内存,否则要面临内存泄露的风险。

C++中new和delete运算符

C++中引入一组关键词以帮助堆对象进行内存管理;他们分别用来创建和撤销堆中的对象。

创建对象:

  1. Person *person = new Person();

当你不用这个对象时,你就要撤销它:

  1. delete person;

事实上,这同样适用于C++中标量类型:

  1. int *x = new int();
  2. *x = 5;
  3. delete x;

你可以认为这些运算相当于Objective-C中的初始化和删除对象。在C++中初始化用的new Person()等同于Objective-C中的[[Person alloc] init]。

但是,在Objective-C中没有等同于delete的运算符。但是我想你已经意识到了,当引用计数归零时,运行时Objective-C对象的存储单元就会被释放。记住,C++不会自动处理引用计数,开发者调用对象完成后负责释放对象。

现在你对C++的内存管理有了大致了解,简言之,在C++中的内存管理要比Objective-C中的要复杂得多。你真的需要考虑下一步是怎样,并且要跟踪对象。



访问栈和堆对象成员

你已经了解到,C++中既可以在栈上也可以在堆上创建对象。然而,这两种方法还有一点微妙但是很重要的区别,即访问成员变量和成员函数的方式稍有不同。

使用栈对象时,你需要点运算符(.);使用堆对象时,你需要使用箭头操作符(-->)。如下:

  1. Person stackPerson;
  2. stackPerson.name = “Bob Smith”; ///< Setting a member variable
  3. stackPerson.doSomething(); ///< Calling a member function
  4. Person *heapPerson = new Person();
  5. heapPerson->name = “Bob Smith”; ///< Setting a member variable
  6. heapPerson->doSomething(); ///< Calling a member function

区别很微妙,但是值得注意。

你还看到箭头操作符与this指针一起用,就像在Objective-C中的self指针一样,它用于类内部函数去访问当前的对象。

下面的C++例子展示了箭头操作符的用法:

  1. Person::doSomething() {
  2. this->doSomethingElse();
  3. }

这会引起一个常见的C++陷阱。在Objective-C中,你可以用空指针调用一个方法,你的应用程序仍会运行的很好:

  1. myPerson = nil;
  2. [myPerson doSomething]; // does nothing

然而,在C++中,如果你要用一个NULL指针调用一个方法或者访问一个实例,你的应用程序会崩溃:

  1. myPerson = NULL;
  2. myPerson->doSomething(); // crash!

因此,你必须确保在C++中不要试图使用空指针。

引用

向函数传递对象时,你传递的是一个对象副本,而不是对象本身。例如,思考下面的C++代码:

  1. void changeValue(int x) {
  2. x = 5;
  3. }
  4. // …
  5. int x = 1;
  6. changeValue(x);
  7. // x still equals 1

很简单,没什么特别的。但是想一想当用一个函数做同样的事情,并且这个函数可以把一个对象作为一个参数。

  1. class Foo {
  2. public:
  3. int x;
  4. };
  5. void changeValue(Foo foo) {
  6. foo.x = 5;
  7. }
  8. // …
  9. Foo foo;
  10. foo.x = 1;
  11. changeValue(foo);
  12. // foo.x still equals 1

这或许令你有些惊讶。仔细想想的话,和简单的int型例子没有不同。在将对象传递给函数之前,创建一个Foo object副本会发生什么情况?不过有时候确实需要传递一个实际对象。一种方法是改变函数指向对象的指针,而不是对象本身。但是无论什么时候调用函数都会产生附加代码。

对比上面列举的值传递的例子, C++定义了一个新的概念来允许通过引用来传递变量。这就意味着不需要创建对象副本。

利用引用传递可以很简单的改变你的调用,你可以在函数签名前简单地在变量前使用ampersand (&)即可,如下:

  1. void changeValue(Foo &foo) {
  2. foo.x = 5;
  3. }
  4. // …
  5. Foo foo;
  6. foo.x = 1;
  7. changeValue(foo);
  8. // foo.x equals 5

它也适用于non-class变量:

  1. void changeValue(int &x) {
  2. x = 5;
  3. }
  4. // …
  5. int x = 1;
  6. changeValue(x);
  7. // x equals 5

引用传递很有用,并能显著提高性能。当创建一个对象副本成本相当高的时候引用传递更加有用,例如使用一个大型链表,创建副本意味着要对对象执行深度复制。



继承

一个面向对象的语言没有继承就不完整。C++当然不会违反这一趋势。思考下面的两个Objective-C类,其中一个类从另一个类继承:

  1. @interface Person : NSObject
  2. @end
  3. @interface Employee : Person
  4. @end

同样的事情可以用C++以很相似的方式表达:

  1. class Person {
  2. };
  3. class Employee : public Person {
  4. };

唯一的区别是在C++中要加一个public关键词。这里Employee类公共的继承Person类。这就意味着person类中的公共成员在Employee类中也是公共类型的。如果用private代替public,那么Person类中的公共成员在Employee类中就将变为私有的。关于这个话题的更多信息,我建议读一篇很棒的关于继承和存储说明符的文章

以上是关于“继承“的简单部分,下面我们开始复杂的部分。与Objective-C不同的是,C++中允许多重继承,即一个类可以继承两个或以上基类。如果你除了Objective-C没有用过其他语言,那么这对你来说一定很陌生。

下面是C++中多重继承的例子:

  1. class Player {
  2. void play();
  3. };
  4. class Manager {
  5. void manage();
  6. };
  7. class PlayerManager : public Player, public Manager {
  8. };

在这个例子中,有两个基类,一个类继承这两个基类。意思是PlayerManager类可以访问每个基类的所有成员变量和函数。简单吧?我确定你已经意识到了,在Objective-C中没有这种方法。

然而,这并不完全正确,对吧?

精明的读者一定注意到在Objective-C中有类似的方法,即protocols(协议)。虽然跟多重继承不太相似,但是两种技术都为了解决同样的问题:提供一个机制来连接两个有相似用途的类。

Protocols(协议)有一个微小的区别,那就是协议没有实现,只是描述类必须遵循哪个接口。

在Objective-C中,上面的例子可被写成:

  1. @protocol Player
  2. - (void)play;
  3. @end
  4. @protocol Manager
  5. - (void)manage;
  6. @end
  7. @interface Player : NSObject <Player>
  8. @end
  9. @interface Manager : NSObject <Manager>
  10. @end
  11. @interface PlayerManager : NSObject <Player, Manager>
  12. @end

当然,这个细小的差别你是能想象的到的。在Objective-C中你要在PlayerManager类中执行play和manager。在C++中你只要在每个基类中实现该方法,然后PlayerManager类会自动的继承每个实现。

虽然,在实践中,多重继承有时会另人混淆和复杂化。对C++开发者来说,多重继承是一个危险的方法,除非绝对必要,开发者会尽量避免使用。

为什么呢?想一想如果两个基类用同样的名字去实现一个函数,并接受同样的参数的话,那么这两个基类就会有同样的原型。在这种情况下,你就需要消除歧义。例如,假设Player和Manager两个类都有一个命名为foo的函数。

你需要这样消除歧义:

  1. PlayerManager p;
  2. p.foo();          ///< Error! Which foo?
  3. p.Player::foo();  ///< Call foo from Player
  4. p.Manager::foo(); ///< Call foo from Manager

这绝对是可行的,但是这增加了混淆,而且最好避免复杂性。这由PlayerManager类的使用者决定。使用协议直接使PlayerManager类实现函数foo,因此这里只有一次实现,没有混淆。

下一步

第一部分中我们了解了C++的简史,如何声明类以及C++中内存管理是如何工作的。当然,关于C++还有很多需要学习的!

第二部分中,在查阅Objective-C和C++标准库之前,学到了更多的高级类的特征和模板。

【转】向iOS开发者介绍C++(-)

时间: 2024-10-06 14:15:55

【转】向iOS开发者介绍C++(-)的相关文章

如何评价苹果中国官网 iOS 8 介绍页面的文案「开发者的大事、大快所有人心的大好事」?[转自知乎]

在什么是「苹果式中文」答案中,小七得出了这个结论: 「苹果式中文」是指句子结构破碎,经常缺乏主语,滥用排比,顶真,偏正短语,和不恰当四字词的广告文体. (有关什么是苹果式中文,小七原来贴错地方了TAT,具体分析请移步:什么是「苹果式中文」?) 那么作为广告翻译,苹果做得如何呢? 首先,有个翻译的概念需要向大家普及: 很多知友提出文案有明显的翻译腔,这个说法其实不准够确啊,有关什么是翻译腔,我的分析请移步:什么是「翻译腔」?「翻译腔」是好是坏? 在这里节选原文一段补充说明: 3. 明明能说中文,非

iOS 开发者旅途中的指南针 - LLDB 调试技术

文章转载于:iOS 开发者旅途中的指南针 - LLDB 调试技术 今天给大家介绍的内容,无关乎任何功能性开发技术,但又对开发的效率影响至深,这就是调试技术. 何为调试呢,比如我们用 print 函数在指定位置进行输出,来定位某些节点的变量内的取值: 12345 let result = parseJSON("[1,2,3]");print(result); result = parseJSON("error");print(result);4 相信我们大家看到类似这

12个ios开发者必备效率工具

这里推荐12个可以帮你简化iOS app开发流程的工具.很多开发者都使用过这些工具,涉及原型和设计.编程.测试.上架以及最后的营销,基本上涵盖了整个开发过程. 原型和设计 有了一个很好的创意后,你要做的不是立刻编程,而是设计UI和创建原型,这样你才能知道app如何运行,根据用户体验需要做哪些调整. App Cooker AppCooker 不仅是一个创建原型的优秀工具,它提供的许多功能还可以帮助你将程序发布到App store中.它集成了Dropbox,Box.net和photo roll,你可

一个iOS开发者对tvOS SDK的初探

http://www.cocoachina.com/ios/20151001/13652.html 作者:Chris Wagner原文地址:tvOS SDK: An iOS Developer’s Initial Impressions 在Walter Isaacson编写的<Steve Jobs>的最后一章中,Steve说过“我最终实现并让它拥有了你们能想象到的最简单的用户界面”.当时他指的就是Apple TV. Steve不幸离世已经过去很久了,但是人们一直翘首以盼着这款产品.就在昨天20

iOS9 Day-by-Day:iOS开发者必须了解的iOS 9新技术与API

iOS9 Day-by-Day是作者Chris Grant新开的一个系列博客,覆盖了iOS开发者必须知道的关于iOS 9的新技术与API,并且还进行了实际操作演练,每篇文章中相关的代码Chris都会将其托管到GitHub. 在第一篇文章中,Chris介绍了iOS 9的三种搜索API: 1)NSUserActivity,索引用户活动以及App的状态. 2)iOS 9新增的CoreSpotlight.framework提供了增.删.改.查等搜索API,可以索引App的内容. 3)Web Markup

iOS SDK 介绍及导入

iOS SDK 介绍 环信 SDK 为用户开发 IM 相关的应用提供的一套完善的开发框架.包括以下几个部分: SDK_Core: 为核心的消息同步协议实现,完成与服务器之间的信息交换. SDK: 是基于核心协议实现的完整的 IM 功能,实现了不同类型消息的收发.会话管理.群组.好友.聊天室等功能. EaseUI: 是一组 IM 相关的 UI 控件,旨在帮助开发者快速集成环信 SDK. 用户可以基于我们提供的 Demo 实现自己的应用,也可以基于 SDK 开发自己应用. SDK 采用模块化设计,每

iOS开发者必备的10款工具

当前iOS和Android两大移动操作系统"二足鼎立",几乎覆盖了市面上大部分的智能手机.相比Android,iOS开发适配更简单,且随着各种实用工具和Swift语言的出现,iOS开发门槛地降低,开发前景巨大.而对于开发者和设计师来说,是否拥有一款功能强大且实用的开发工具将直接影响其工作效率和成果.为此,我们结合现有资讯.整合最新资源,从开发.设计.分析.内测分发等方面整理出"iOS开发者必备的十款工具"供大家参考. 开发环境: 1)Xcode 作为苹果公司的当家I

[转] iOS开发者的Weex伪最佳实践指北

[From] http://www.cocoachina.com/ios/20170601/19404.html 引子 这篇文章是笔者近期关于Weex在iOS端的一些研究和实践心得,和大家一起分享分享,也算是对学习成果的总结.文章里面提到的做法也许不是最佳实践,也许里面的方法称不算是一份标准的指南手册,所以标题就只好叫"伪最佳实践指北"了.有更好的方法欢迎大家一起留言讨论,一起学习. 由于笔者不太了解Android,所以以下的文章不会涉及到Android. 一. React Nativ

关于iOS开发者账号功能总结-真机调试(二)

转载请注明出处:http://blog.csdn.net/dengbin9009/article/details/43970059 在上篇文章中说道如何从开发者网站创建App ID,添加Device,创建并下载开发证书,创建并下载配置文件,详情见:关于iOS开发者账号功能总结-真机调试(一). 在这篇文章中将要介绍的时如何利用下载号的证书cer文件和配置文件mobileprovision. 上篇文章我们已经将证书和配置文件下载到桌面,不过为了Mac的贞洁(笔者处女座),已经把两个文件放到特定的文