OC与C++的区别参考汇总:
C++与OC同为面向对象设计语言,他们有很多相似的对象,但是由于两者属于不同的面向对象学派。因此两者之间的比较也乐此不疲。下面简要说说他们的区别:
1.继承:Objective-C与同Java和Smalltalk一样不支持多重继承,而C++语言支持多重继承(从侧面可以说明多重继承的效率不高);
2.函数调用:Objective-C通过互相传递消息实现函数调用,而C++直接进行函数调用
3.定型:Objective-C是动态定型(dynamicaly typed)。所以它的类库比C++要容易操作。Objective-C 在运行时可以允许根据字符串名字来访问方法和类,还可以动态连接和添加类。而C++,对象的静态类型决定你是否可以发送消息给它。
4.接口:Objective-C采用protocol协议(非正式和正式)的形式来定义接口,而C++采用虚函数的形式来定义接口。
5.方法重载:c++中允许两个方法的名字相同,参数个数相同,但是参数类型不同,以及不同的返回值类型。而OC中不允许同一个类中两个方法有相同的名字,参数个数相同,参数类型不同。
总结
1.C++存在多继承,OC没有,取而代之的有代理,类别,通知等较为常见的设计模式,从而导致思维方式的不同。
2.C++有公有和私有变量,OC没有私有变量一说,当需要访问OC类成员变量,就需要将类成员设置成类属性。
3.C++多态允许同一个类中两个方法有相同的名字,参数个数相同,参数类型不同。而OC不可以。
4.C++采用虚函数的形势定义接口,OC采用协议。
c++与oc的对比
我觉得最主要的区别就是,c++中的对象可以是动态创建的(使用new来创建,返回对象的地址),也可以是直接的创建对象(如:A a;就是直接创建)。
所以在c++中就有了 值传递 与指针传递的区别。在java中是没有的,oc中也是没有的,因为java与oc创建的对象都相当于是创建的对象的指针,而不能直接的创建对象本身,都是通过这个指针去访问对象的。
比如,我在c++中创建一个B对象,可以用2种方式:
1,
class A: //学生对象
{
public:
int id;
string name;
int age;
string sex;
};
第一种创建B的方法:
class B:
{
public:
int classId; //班级编号
A a; //学生对象数组
} //这种方式创建,是直接创建的A a对象,B b;那么当B对象创建的时候,就为a对象分配了空间,也就是a对象被初始化好了。Hzh:隐式调用默认普通构造函数
第二种创建B的方法:
class B:
{
public:
int classId; //班级编号
A *a; //学生对象数组的指针
} //这种方式创建,只创建了一个A对象的指针,所有指针都是4个字节,这个时候其实a对象是个null,并没有分配内存。只有当a = new A();的时候才为a分配了内存。
总结:
可以看出,第一种方法就是非动态的创建内存的例子,使用A a;直接的分配内存。第2种方法就是动态分配内存的例子,使用A *a;当需要初始化a的时候再用new为它分配内存。
很显然,第2种方法要好得多。当需要的时候再分配内存,可以提高内存的使用效率。而objective-c就是这种方法的忠实拥护者,在oc中只能动态的创建内存,这也是oc的动态特性之一。
我们假设A对象占20个字节,
如果按照第一种B类的写法。B b;创建B对象,b占用的内存空间是20 + 4,也就是24个字节。
如果按照第二种B类的写法。B b;创建B对象,b占用的内存空间是4 +4,也就是8个字节,这是因为A *a是一个指向a对象的指针,只占4个字节,这个时候,b.a是null,只有当在需要使用b.a的时候,为a对象初始化。b.a = new A();。这也就是oc中的懒惰初始化,也叫延迟初始化。
在oc中只能用第二种方法初始化,第一种方法是没有的。
只有当类中有指针的时候,才能延迟初始化,
char *pBuffer; //类的对象中包含指针,指向动态分配的内存资源
int nSize;
};只有这种情况下,pBuffer会在需要的时候去动态的申请内存。
再来谈谈c++的拷贝构造函数:
在C++中,下面三种对象需要调用拷贝构造函数(有时也称“复制构造函数”):
1) 一个对象作为函数参数,以值传递的方式传入函数体;
2) 一个对象作为函数返回值,以值传递的方式从函数返回;
3) 一个对象用于给另外一个对象进行初始化(常称为复制初始化)
务必要注意区分这3种情况与OC种的区别,在OC中是根本不存在这3种情况的。因为oc中只能创建对象的指针,是不能直接创建对象的。上面的3种情况都是针对与直接操作一个对象的。
比如:
B getBObj(B bb)
{
return bb;
}
{
B b1;
B b2 = b1.getObj(b1); //直接传对象,非对象指针
//传进去的b1是实参,bb其实是调用拷贝构造函数拷贝出来的b1的一个副本,当返回bb的时候,b2接受的是bb对象的一个拷贝副本,因此内存中有b1,bb,b2对象,当getObj调用完毕,bb对象被释放,内存中还有b1,b2两个对象。所以注意:bb并不是b1对象,b2并不是bb对象就可以了。
b2 = b1;//直接用对象赋值,而非对象指针 //Hzh:赋值拷贝构造函数,运算符重载.
正常情况下,编译器会创建隐式的拷贝构造函数,那什么时候需要重写拷贝构造函数呢?
class CExample
{
public:
CExample(){pBuffer=NULL; nSize=0;}
~CExample(){delete []pBuffer;}
void Init(int n){ pBuffer=new char[n]; nSize=n;}
private:
char *pBuffer; //类的对象中包含了指针,指向动态分配的内存资源
int nSize;
};
这中情况下,如果直接调用默认的拷贝构造函数,那么拷贝的过程中,pBuffer拷贝的话,只拷贝了一个指针,这个指针指向一块内存,这块内存并没有拷贝。//Hzh:浅拷贝
CExample obj1;
obj1.Init(40);
CExample obj2 = obj1;
即它们将指向同样的地方,指针虽然复制了,但所指向的空间并没有复制,而是由两个对象共用了。这样不符合要求,对象之间不独立了,并为空间的删除带来隐患。当obj1删除pBuffer这块内存的时候,obj2的这块内存也没有了,obj2的pBuffer指针指向的内存也被释放了。这中情况下就必须要重写拷贝构造函数,在这个函数中,把pBuffer这块内存也要复制。
CExample::CExample(const CExample& RightSides) //拷贝构造函数的定义
{
nSize=RightSides.nSize; //复制常规成员
pBuffer=new char[nSize]; //复制指针指向的内容 //Hzh:深拷贝
memcpy(pBuffer,RightSides.pBuffer,nSize*sizeof(char));
}
只有这样重写拷贝构造函数之后,拷贝的时候就没有问题了。
这中情况就类似于objective-c中的对象拷贝。对象深拷贝的话,就必须要实现copy方法,在这个方法中重新真正的copy,所以c++中的默认拷贝构造函数就类似oc中的浅拷贝,也就是指针拷贝。
需要注意的一点就是:
oc中虽然不能直接的创建对象,但是oc中函数调用,以及返回的时候,传入的参数一样会拷贝一份出来,只不过这个拷贝是一个指针,而不是拷贝的一个对象。返回的时候一样是拷贝的一个副本,这个副本是指针。
正是因为c++中有直接创建对象与创建对象指针的区别。所以才产生了值传递,与地址传递的区别。它可以传递一个对象,也可以传递这个对象的地址。如果传递的是对象本身,就是值传递。如果传递的是对象的指针,就是指针传递。其实指针传递也是传递的值,传递的是变量的地址值。
但是在oc与java中,只能传递对象的地址。所以就不用区分上面的情况了。
其实c++中指针传递也是传递的值,传递的是变量的地址值,这个地址值是一个对象的首地址。
与C++的不同之处有:
O-C中所有的类都必须继承自NSObject。
O-C中所有对象都是指针的形式。
O-C用self代替this。
O-C使用id代替void*。//Hzh:id既可用于函数参数类型也可用于函数的返回值类型,而instancetype只可用于函数的返回值
O-C使用nil表示NULL
O-C只支持单继承。
O-C使用YES/NO表示TRUE/FALSE
O-C使用#import代替#include
O-C中用消息表示类的方法,并采用[aInstance method:argv]调用形式。
O-C支持反射机制
O-C支持Dynamic Typing, Dynamic Binding和Dynamic Loading。
与C++的相同之处有:
与C共享的部分一致。
可以使用assert(BOOL), 一般用NSCParameterAssert(BOOL)代替。
O-C中的命名前缀说明:
NS-:NextStep
CF-:Core Foundation
CA-:Core Animation
CG-:Core Graphics
UI-:User Interface
O-C中的消息特殊性:
调用消息的类可以不知道如何响应这个消息。如果它不知道如何处理这个消息,它会自动的将这个消息转给其他的类,比如它的父类。
调用消息的类可以是nil。在C++中,在使用类方法之前,我们都需要检查对象是否为空,所以在实现析构函数的时候,常会有如下的代码,如if (var) { delete var; }但是在objective c中,我们就可以直接写[var release];即使var == nil, 也不会有问题。
O-C中的函数声明格式有:
-/+ (return type) function_name;
-/+ (return type) function_name : (parameter type) parameter;
-/+ (return type) function_name : (parameter type) parameter1 otherParameter: (parameter_type) parameter2
以上参数说明:-表示一般函数,+表示静态函数。otherParameter是参数的别名(第一个参数的别名省略),在函数调用时方便指定。
O-C中的构造/析构函数
O-C中的init()/release()对应于C++的构造/析构函数。alloc()/dealloc()也就对应于C++的new和delete,其中的dealloc()由于引用计数的自动调用而不用手动调用。
O-C中父类的init()/release()函数需要子类的手动调用。而且每次都必须调用。不同于C++的自动调用。
构造函数(- (id) init)调用形如:CSample* pSample=[CSample alloc] init];其中alloc(+ (id) alloc)是继承来的static函数,init是继承来的一般函数,如重写一般函数时,则相当于C++的覆盖(不带参数)或重载(带参数)。
析构函数(- (void) release)将引用计数减1,当=0时父类的release()会自动调用dealloc(- (void) dealloc);
当O-C没有数据成员时,可省略{},建议保留。
继承下来的方法,如:-(id) init可以头文件中省略,建议保留
O-C中只有数据成员的访问限制,没有方法的访问限制。
同C++一样,数据成员有三种访问限制public, protected, private,缺省是protected。
示例:@interface AccessExample: NSObject {
@public
int publicVar;
@protected
int protectedVar;
@private
int privateVar;
}
@end
方法的访问限制可通过Category实现
示例:
@interface MyClass
- (void) sayHello {
NSLog(@"Hello");
}
@end
@interface MyClass(Private)
- (void) kissGoodbye;
@end
O-C中没有类的静态变量,只有全局变量
O-C中的数组NSArray可以保存不同类型的数据。
O-C也支持run-time时的类类型检查
- (BOOL) isKindOfClass: classObj
用于判断该对象是否属于某个类或者它的子类
- (BOOL) isMemberOfClass: classObj
用于判断该对象是否属于某个类(这里不包括子类)
- (BOOL) respondsToSelector: selector
用于判断该对象是否能响应某个消息。这里,我们可以将@selector后面带的参数理解为C++中的函数指针。
注意:1)不要忘了@ 2)@selector后面用的是(),而不是[]。3)要在消息名称后面跟:,无论这个消息是否带参数。如:[pSquare respondsToSelector:@selector(Set: andHeight:)]。
+ (BOOL) instancesRespondToSelector: selector
用于判断该类是否能响应某个消息。这是一个静态函数。
-(id) performSelector: selector :调用对象的selector方法。
conformsToProtocol 类似于respondsToSelector ,用于动态检查某个对象是否遵守某个协议。
Category:在没有源代码的情况下,为一个已经存在的类添加一些新的功能
只能添加新的方法,不能添加新的数据成员
Category的名字必须是唯一的
Protocol:相当于C++中的纯虚类
形如:@interface MyDate: NSObject <Printing> { } @end
使用:MyDate * dat = [[MyDate alloc] init];id<Printing> var = dat; [var print]。
说明:我们首先声明了Printing 协议,任何遵守这个协议的类,都必须实现print 方法。在Objective C 中,我们通过<>来表示遵守某个协议。当某个类声明要遵守某个协议之后,它就必须在.m文件中实现这个协议中的所有方法。使用id<Printing> 作为类型,而不是象C++中的Printing* var。
IBOutlet, IBAction: 这两个东西其实在语法中没有太大的作用。如果你希望在Interface Builder中能看到这个控件对象,那么在定义的时候前面加上IBOutlet,在IB里就能看到这个对象的outlet,如果你希望在Interface Builder里控制某个对象执行某些动作,就在方法前面加上(IBAction)。
尽量避免在一行语句中进行两层以上的嵌套
消息转发:- (void) forwardInvocation: (NSInvocation*)anInvocation;
4扩展的关键字
编辑
@interface
类型声明,类似于c++中的class,区别在于Object c中的声明与实现是强制分开的,@interface关键字用于类型的声明,包括数据成员、方法声明、属性等。方法的参数传递采用中缀符的形式,利用“:”分割参数名和被传递参数,类型的声明以@interface开头,以@end结束,通常一个类型的声明采用下面的结构:
@class someOtherObject //外部类型声明
@interfacesomeObject:NSObject //继承的类型
{
int i; //成员变量
}
-(id)someMethod:(int)someArg someOtherArgName:(int)someOtherArg; //对象的方法
+(id)someMethod:(int)someArg; //类方法
-(id)init; //初始化方法
@propertyint num; //属性
@end
@implementation
对应于@interface的类型声明,@implementation表示一个类型的实现过程,同样以@end结束,实现的格式通常如下:
@implementationsomeObject
-(id)someMethod:(int)someArg someOtherArgName:(int)someOtherArg
{
//实现代码
}
@synthesize num=i; //将属性与变量进行对应
@end
new、alloc
Object C中的方法调用形式采用消息发送的方式,通常调用的形式如
[someObject someMethod:firstArg someOtherArgName:otherArg]
实例的初始化也采用消息发送的形式,可以简单的调用类型的new方法来获取一个实例对象,简单实例化的方法通常是:
someObject *obj = [someObjectnew]; //类的实例化
new方法的实际过程是调用alloc和init方法,因此如果需要采用自定义的方法来初始化实例,则需要自己重写init方法,通常的初始化方式为:
someObject *obj = [[someObject alloc] init]; //采用无参数的init实例化
someObject *obj = [[someObject alloc] initWithArg:Arg]; //采用参数的实例化
@class
@class是一个前向引用声明,类似于C++中的friend友元声明,其作用是告诉编译器其后面的字段代表一个类型名称,尽管不知道类型的具体实现,但是只需要将其作为一个类型名称处理即可。通常在使用复合的结构时可以采用@class来减少头文件的相互引用,如果出现循环依赖,则需要依靠@class来避免引用的死循环。通常使用形式为:
@classsomeOtherObject;
@interfacesomeObject:NSObject
{
someOtherObject *obj;
}
@end
@property
尽管可以使用obj->arr的形式去强制读取对象的成员变量,但是良好的编程形式是对外界提供成员变量的读写接口。@property关键字提供了外界对成员变量的访问接口,其本质是为某一个属性提供set和get操作。根据不同的需要,可以添加readonly(只读,相当于只添加get不添加set方法)或者readwrite(读写,如果不添加则为默认);还有三种赋值方式可选[2] :assign(直接赋值,通常用于基本类型),retain(释放旧值,增加新的retaincount),copy(常用于字符串,生成一个新的拷贝)。通常使用的方式如下:
@interface someObject:NSObject
{
int i; //成员变量
}
@property (assign,readonly) int num; //属性
@end
@synthesize
与@property对应,将一个外在属性与成员变量相关联,定义在@implementation中,如果属性名与变量名一致则可以省略变量名。常用方法:
@implementation someObject
@synthesize num=i;//如果属性名也为i,则可以直接写为 @synthesizei
@end
内存管理
Object C采用引用计数的方式进行内存管理,由于所有的对象都继承于NSObject,因此所有的对象都可以接受NSObject的三个方法:
-(id)retain;
-(void)release;
-(unsigned)retainCount;
retain方法将对象的引用计数加一并返回该对象,release将引用计数减一,retainCount方法返回对象当前的引用计数。当采用new、alloc、copy方法创建一个对象时,它的引用计数被置为1,如果程序中对该对象进行操作,则应根据需要,通过调用retain和release方法来保证该对象在不需要的时候被清除。当一个对象的引用计数被置为0后,系统会自动向对象发送一个dealloc消息,将其占有的资源释放。通常情况下,如果一个对象的初始化过程调用了其他资源,则应该重写改对象的dealloc过程,保证在对象的销毁期正确释放这些资源。
为了更加方便的进行能存管理,cocoa中提供了一个自动释放池(autorelease pool)的概念,每一个类都继承了一个autorelease方法,当调用对象的autorelease方法时,改对象会被加入到开始创建的自动释放池中。当程序进行到不再需要自动释放池中的对象时,将自动释放池释放的时候会向池中的所有对象发送一个release消息,从而保证不再需要的对象被正确的释放。通常的用法如下:
NSAutoreleasePool *pool;
pool = [[NSAutoreleasePool alloc] init];
someObject * obj = [[someObject alloc] init];
[obj autorelease]; //加入自动释放池
//其他代码
[pool release]; //执行该语句时,系统会向池内所有的对象发送release消息;在这个例子中,如果对obj进行的其他retain操作和release操作保持一致的话,则会将obj的引用计数变为0,从而调用它的dealloc方法进行资源释放
Object C进行内存管理的3条规则是:
如果使用new、alloc或copy操作获得一个对象,则该对象的保留计数器值为1
如果通过任何其他方法获得一个对象,则假设该对象的保留计数器值为1,而且已经被设置为自动释放
如果保留了某个对象,则必须保持retain方法和release方法的使用次数相等。
类别
类别是为现有的类提供一个新的方法的方法,即使没有一个类的源代码,仍然可以向类中添加一个方法以方便使用。类别的主要目的有3个:将类的实现分散到多个不同的文件或框架中,创建对私有方法的前向引用,向对象添加非正式协议。
类别的声明方法:
@interfacesomeObject (someProtocal)
-(void)someMethod;
@end
类别的实现方法:
@implementationsomeObject(someProtocal)
-(void)someMethod
{
}
@end
@protocol
Object C中的协议类似于java中的接口,通过@protocol关键字定义一个或多个需要遵从协议的对象实现的方法,协议定义的方法:
@protocolsomeProtocol
-(void)someMethod;
@end
采用协议的方法是在类声明时使用尖括号注明其需要使用的协议:
@interfacesomeObject:NSObject <someProtocol>
在类实现时需要将协议中规定的方法都予以实现。
Object C 2.0增加了2个新的协议修饰符@optional和@required,可以规定协议中的方法是否为必须实现的方法。