从C/C++到Objective-C(二)--- 面向对象

时间:2021-05-02 19:52:33

    OC和C++对C的扩展最重要的当然就是“面向对象”了,学习了C++对面向对象自然对面向对象一点也不会感到陌生了,可能还觉得有点亲切呢,陌生的语言中看到熟悉的词,你说亲不亲切啊!面向对象的几个重要概念不外乎就是类,抽象,封装,多态了, 思想都差不多的,主要就是实现的具体代码不同罢了。面向对象与面向过程不同,前者是以程序的数据为中心,函数为数据服务。

    OC中有个概念叫“间接”,意思就和这个字面意思差不多,比如说基本的变量就是间接的一种实际运用,例如:int number = 8; 这里的number就代表了数字8,当然也可以给它赋不同的值,它也就代表不同的数字,这里的number就代表了你给它赋的值。“间接”所指的就是这样一种关系。我理解的是,这里的间接其实也就是抽象的一种运用罢了,很大的问题抽象出几个间接层来,问题就被分小了,当然解决起来也就容易多了。

    这里给一个程序,是文件名的间接,这个还是挺好理解的。贴这段代码是想表达,OC是对C的扩展,所以C中的很多东西都是可以直接用的,当然头文件得换换了。

#import <Foundation/Foundation.h>

int main (int argc, const char * argv[])
{
FILE *wordFile = fopen ("/tmp/words.txt", "r");
char word[100];

while (fgets(word, 100, wordFile)) {
// strip off the trailing \n
word[strlen(word) - 1] = '\0';

NSLog (@"%s is %d characters long",
word, strlen(word));
}

fclose (wordFile);

return (0);

} // main
main函数的用法也和C中是一样的,argc代表命令行中输入的参数个数,默认的是1,代表的就是程序本身的名字,如果在命令行中输入一个参数那就是argv[1]了。

    下面就主要说说OC中面向对象编程的具体代码了。

    先贴一段代码,主要说说与C中的两个不同点:

void drawShapes (id shapes[], int count)
{
int i;

for (i = 0; i < count; i++) {
id shape = shapes[i];
[shape draw];
}

} // drawShapes
这里的id是OC中的标识符。id是一种泛型,可以用来引用任何类型的对象,我觉得有点类似C中void * 的用法,使用void*代表指向任何数据类型的指针。还有一个就是中括号[ ]的用法,[shape draw]; 这里就不在是C中用来代表数组的符号了,在OC中中括号这样的用法是用来通知某个对象该去做什么。中括号里的第一项代表的是对象,其余部分则是需要对象执行的操作。OC中通知对象执行某种操作称为发送消息,通俗点说就是调用该对象的方法。这里的[shape draw]; 表示向shape对象发送了draw消息,其实也就是调用shape对象中的draw方法。

    感觉OC中比C++更加明显的区分了接口和实现这两个概念。用两个编译器指令@interface和@implementation区分了类中接口和实现。通过这两个指令分别把相关的信息传递给编译器。

    首先看看接口代码:

@interface Circle : NSObject
{
ShapeColor fillColor;
ShapeRect bounds;
}

- (void) setFillColor: (ShapeColor) fillColor;

- (void) setBounds: (ShapeRect) bounds;

- (void) draw;

@end // Circle
    初次看着是会觉得有些奇怪,和C++中还是有些不一样的。在OC中只要看到@符号就可以把它当成是对C语言的扩展,这里的@interface Circle告诉编译器:这是新类Circle的接口。大括号中的两行内容就是类Circle中的数据成员,这点和C++中类似的,创建出得Circle新对象都是有着两个元素构成的,ShapeColor和ShapeRect分别是枚举和结构体,最下面贴有具体的代码。这里fillColor和bounds的值称为Circle类的实例变量。

    下面带短线的三行内容就是类Circle中的方法声明。其中前面的短线表明这是OC中方法的声明。这是OC中区分函数原型与方法声明的一种方式,函数原型中没有先行短线,例如上面的drawShapes()函数。第一个小括号中的void是返回类型,void表示不返回任何值。setFillColor:代表该方法的名称,注意结尾处的冒号是名称的一部分,它告诉编译器和编程人员后面会出现方法的参数。(ShapeColor)fillColor,参数的类型为ShapeColor,在小括号中指定,fillColor就是参数名了,在后面方法的具体实现中可以使用该名称来引用参数。当然你可能也注意到了后面的draw方法是不带参数的,所以也就没有加冒号:了,冒号是方法名称的重要组成部分,如果方法使用了参数,则需要加冒号,否则就不需要冒号。最后面的@end则是告诉编译器已经完成了Circle类的声明。
    OC中有一种名为中缀法的语法技术,方法的名称及其参数都是合在一起的。比如:

    [circle setFillColor: KRedColor];  表示调用circle对象的setFillColor方法,并且带了KRedColor参数。

调用带两个参数的方法则为:[textThing setString:@"hello world" color:kBlueColor]; 这个理解起来就稍微有些困难了,首先同样的,textThing为对象名,那函数名呢?如果非要说个函数名的话,那就应该是setString:color:了,这里的setString: 和color:分别是参数@“hello world"和kBlueColor的名称。这就是中缀法添加的东西了。如果这个方法的返回值为void那转成oc的函数声明语法大概就是:

    -(void) setString:(NSString*) strValue color:(ShapeColor) colorValue;

那转成C++中的方法声明大概就是:

    void setStringcolor (NSString* strValue, ShapeColor colorValue);

这样解释后对中缀法应该就会好理解一些了。

    刚才我们说了Circle类的@interface部分,主要就是用来定义类的公共接口的,接下来就说下类的@implementation实现部分了。同样先贴代码:  

@implementation Circle

- (void) setFillColor: (ShapeColor) c
{
fillColor = c;
} // setFillColor

- (void) setBounds: (ShapeRect) b
{
bounds = b;
} // setBounds

- (void) draw
{
NSLog (@"drawing a circle at (%d %d %d %d) in %@",
bounds.x, bounds.y,
bounds.width, bounds.height,
colorName(fillColor));
} // draw

@end // Circle
    @implementation的作用和@interface类似,也是一个编译器指令,表明为Circle类提供实现代码。定义的方法和规则都是和C++中的类似,定义顺序不一定按照声明顺序来。这里的把setFillColor和setBounds的参数名换成了c和b,目的是为了把上面定义的实例变量和参数变量区分看来,避免覆盖掉定义的实例变量。这里和C++还是有些不同的,C++中的数据成员变量名和方法参数名一样也可以。

    上列代码中的 fillColor=c; 这里有一个类似C++中this指针的参数self,所以这句代码还可以这样写 self -> fillColor = c; self和this指针一样也是隐藏的。

    最后就是实例化对象了:

int main (int argc, const char * argv[]) 
{
id shape;

ShapeRect rect0 = { 0, 0, 10, 30 };
shape = [Circle new];
[shape setBounds: rect0];
[shape setFillColor: kRedColor];
}
在创建一个新对象时,向类发送new消息,也就类似C++中调用new方法,调用后就生成了一个实例对象。

    下面是整个例子的详细代码:

#import <Foundation/Foundation.h>

// --------------------------------------------------
// constants for the different kinds of shapes and their colors

typedef enum {
kRedColor,
kGreenColor,
kBlueColor
} ShapeColor;


// --------------------------------------------------
// Shape bounding rectangle


typedef struct {
int x, y, width, height;
} ShapeRect;


// --------------------------------------------------
// convert from the ShapeColor enum value to a human-readable name

NSString *colorName (ShapeColor color)
{
switch (color) {
case kRedColor:
return @"red";
break;
case kGreenColor:
return @"green";
break;
case kBlueColor:
return @"blue";
break;
}

return @"no clue";

} // colorName


// --------------------------------------------------
// All about Circles

@interface Circle : NSObject
{
ShapeColor fillColor;
ShapeRect bounds;
}

- (void) setFillColor: (ShapeColor) fillColor;

- (void) setBounds: (ShapeRect) bounds;

- (void) draw;

@end // Circle


@implementation Circle

- (void) setFillColor: (ShapeColor) c
{
fillColor = c;
} // setFillColor

- (void) setBounds: (ShapeRect) b
{
bounds = b;
} // setBounds

- (void) draw
{
NSLog (@"drawing a circle at (%d %d %d %d) in %@",
bounds.x, bounds.y,
bounds.width, bounds.height,
colorName(fillColor));
} // draw

@end // Circle




// --------------------------------------------------
// All about Rectangles

@interface Rectangle : NSObject
{
ShapeColorfillColor;
ShapeRectbounds;
}

- (void) setFillColor: (ShapeColor) fillColor;

- (void) setBounds: (ShapeRect) bounds;

- (void) draw;

@end // Rectangle


@implementation Rectangle

- (void) setFillColor: (ShapeColor) c
{
fillColor = c;
} // setFillColor


- (void) setBounds: (ShapeRect) b
{
bounds = b;
} // setBounds


- (void) draw
{
NSLog (@"drawing a rectangle at (%d %d %d %d) in %@",
bounds.x, bounds.y,
bounds.width, bounds.height,
colorName(fillColor));
} // draw

@end // Rectangle


// --------------------------------------------------
// All about OblateSphereoids

@interface OblateSphereoid : NSObject
{
ShapeColorfillColor;
ShapeRectbounds;
}

- (void) setFillColor: (ShapeColor) fillColor;

- (void) setBounds: (ShapeRect) bounds;

- (void) draw;

@end // OblateSphereoid


@implementation OblateSphereoid

- (void) setFillColor: (ShapeColor) c
{
fillColor = c;
} // setFillColor


- (void) setBounds: (ShapeRect) b
{
bounds = b;
} // setBounds


- (void) draw
{
NSLog (@"drawing an egg at (%d %d %d %d) in %@",
bounds.x, bounds.y,
bounds.width, bounds.height,
colorName(fillColor));
} // draw

@end // OblateSphereoid



// --------------------------------------------------
// Draw the shapes

void drawShapes (id shapes[], int count)
{
int i;

for (i = 0; i < count; i++) {
id shape = shapes[i];
[shape draw];
}

} // drawShapes



// --------------------------------------------------
// The main function. Make the shapes and draw them

int main (int argc, const char * argv[])
{
id shapes[3];

ShapeRect rect0 = { 0, 0, 10, 30 };
shapes[0] = [Circle new];
[shapes[0] setBounds: rect0];
[shapes[0] setFillColor: kRedColor];

ShapeRect rect1 = { 30, 40, 50, 60 };
shapes[1] = [Rectangle new];
[shapes[1] setBounds: rect1];
[shapes[1] setFillColor: kGreenColor];

ShapeRect rect2 = { 15, 19, 37, 29 };
shapes[2] = [OblateSphereoid new];
[shapes[2] setBounds: rect2];
[shapes[2] setFillColor: kBlueColor];

drawShapes (shapes, 3);

return (0);

} // main


参考书籍:《Objective-C 基础教程(第二版)》