C語言和C++都會(huì)經(jīng)過匯編,生成匯編代碼,在匯編代碼的階段,是分辨不出是C語言還是C++語言的。在早期C++還沒有成熟的編譯器的時(shí)候,C++通過先把程序翻譯成C語言,然后使用C語言的編譯器把程序編譯成可執(zhí)行文件。所以,面向?qū)ο笾皇且环N思想,在不支持面向?qū)ο蟮恼Z言中也可以使用這種思想。只不過,在C語言中,需要自己實(shí)現(xiàn)這些特性,而在C++中,是編譯器在幕后做了一些工作。
數(shù)據(jù)抽象
數(shù)據(jù)抽象可以將類的接口和實(shí)現(xiàn)分開。C語言中有struct來定義新的數(shù)據(jù)類型,但沒有實(shí)現(xiàn)數(shù)據(jù)類型和操作的綁定,要實(shí)現(xiàn)操作和數(shù)據(jù)類型的綁定,首先在傳入?yún)?shù)的時(shí)候,需要把struct的地址傳進(jìn)去,然后把所有的操作都變成函數(shù)指針存儲(chǔ)在結(jié)構(gòu)體中。以一個(gè)簡(jiǎn)單的類為例:
#ifndef _POINT_H_
#define _POINT_H_
typedef struct point* Point;
struct point{
int x;
int y;
double (*distance)(Point p1);
void (*print)(Point p);
};
double distance(Point p1);
Point create_point(int x,int y);
void print_point(Point p);
#endif
#include "point.h"
#include <stdlib.h>
#include <math.h>
#include <stdio.h>
void print_point(Point p){
printf("x = %d, y = %d.\n",p->x,p->y);
}
double distance(Point p1){
return sqrt(pow(p1->x,2) + pow(p1->y,2));
}
Point create_point(int x,int y){
Point p = (Point)malloc(sizeof(struct point));
p->x = x;
p->y = y;
p->distance = distance;
p->print= print_point;
return p;
}
int main(int argc, char const *argv[])
{
Point p = create_point(1,1);
p->print(p);
printf("p distance = %f\n",p->distance(p));
free(p);
return 0;
}
這樣,對(duì)象的創(chuàng)建和方法的調(diào)用就有一點(diǎn)面向?qū)ο蟮母杏X了。
繼承
使用繼承,可以定義相似地的類型并對(duì)其關(guān)系建模。繼承就是對(duì)類的擴(kuò)展,為了能夠?qū)崿F(xiàn)兼容,派生的類必須有基類的所有成員和函數(shù)。然后可以在其基礎(chǔ)上增加一些功能,也可以改變?cè)械墓δ?。這里我簡(jiǎn)單的把基類的函數(shù)指針和成員放在派生類的前面。
#ifndef _CIRCLE_H_
#define _CIRCLE_H_
#include "point.h"
typedef struct circle* Circle;
struct circle{
int x;
int y;
double (*distance)(Point p1);
void (*print)(Circle c);
int r;
double (*area)(Circle c);
};
Circle create_circle(int x,int y,int r);
double area(Circle c);
#endif
#include "circle.h"
void print_circle(Circle c){
printf("x = %d, y = %d, r = %d.\n",c->x,c->y,c->r);
}
Circle create_circle(int x,int y,int r){
Circle c = (Point)malloc(sizeof(struct circle));
c->x = x;
c->y = y;
c->distance = distance;
c->print = print_circle;
c->r = r;
c->area = area;
return c;
}
double area(Circle c){
return 3.14 * c->r * c->r;
}
int main(int argc, char const *argv[])
{
Circle c = create_circle(1,1,2);
printf("c distance = %f\n",c->distance(c));
c->print(c);
printf("c area = %f\n",c->area(c));
return 0;
}
這里可以發(fā)現(xiàn),把point的成員放在前面,然后在初始化的時(shí)候,將函數(shù)指針賦值為point的函數(shù)地址,如果覆寫了,就改為改過之后的函數(shù)地址。可以發(fā)現(xiàn),能夠調(diào)用原來的函數(shù),也能夠調(diào)用新增的函數(shù)。
動(dòng)態(tài)綁定
動(dòng)態(tài)綁定,可以在一定程度上忽略相似類型的區(qū)別,而以統(tǒng)一的方式使用它們的對(duì)象??聪旅嬉粋€(gè)例子:
int main(int argc, char const *argv[])
{
Circle c = create_circle(1,1,2);
Point p = (Point)c;
p->print(p);
printf("p distace = %f\n",p->distance(p));
return 0;
}
可以發(fā)現(xiàn),可以把Circle轉(zhuǎn)化為Point,并且可以正常的調(diào)用Point的方法。這里其實(shí)很簡(jiǎn)單,強(qiáng)制裝換之后,其實(shí)和沒有裝換是沒太大區(qū)別的,除了不能訪問其擴(kuò)展的成員。其實(shí)動(dòng)態(tài)綁定不在調(diào)用的時(shí)候,而在初始化的時(shí)候,print函數(shù)在初始化的時(shí)候就變成了新的地址,所以這里調(diào)用的時(shí)候,會(huì)使用Circle的函數(shù)。
總結(jié)
最近在看《深度探索C++對(duì)象模型》,一方面感覺C++編譯器的實(shí)現(xiàn)確實(shí)很巧妙,一方面又感覺C語言確實(shí)小巧精悍,即使不用C++,C語言本身也可以實(shí)現(xiàn)面向?qū)ο蟮臇|西。所以寫了這一篇筆記,加深一下自己對(duì)面向?qū)ο蟮睦斫?。個(gè)人水平有限,舉的例子也許有一些問題,望大家見諒。