面试官:C和C++有什么不同?
应聘者:一个是面向过程,一个是面向对象。
这样的答案在我看是不完全正确,因为面向过程编程和面向对象编程是编程思想,C++可以用面向过程的思想编程,同样,C语言也可以用面向对象的思想编程。可能很少有人想过用C语言实现面向对象编程(真的麻烦,感觉就是吃力不讨好),但是,通过这样的尝试,可以使我们的学习过程更有意思。也能通过用C语言实现面向对象编程来深化对封装、继承、多态的理解(这是我个人的感觉)。以下的例子可能过于简单或不够全面(只实现了一部分),如果想再深入研究,建议上网百度相关的知识。
在学习如何使用C语言完成面向对象编程之前,先来看函数指针的用法。
#include
char (*p)(int);
char Fun(int a)
{
return a;
}
int main()
{
p = Fun;
printf("%c\n",(*p)(75));
return 0;
}
复习了函数指针,接下来尝试用C语言中实现C++中的类,类似于:
class Asd
{
public:
int a,b;
int finda(){return a;}
int findb(){return b;}
int ab(){return a+b;}
};
具体实现如下:
#include
#include
struct Asd;
struct Asd_option//Asd类的函数成员
{
int (*get_a)(struct Asd*);
int (*get_b)(struct Asd*);
int (*ab)(struct Asd*);
};
typedef struct Asd//Asd类
{
int a;//公有数据成员
int b;//公有数据成员
struct Asd_option *a_option;//函数指针,指向的是操作
}Asd;
int finda(struct Asd *s)//具体操作
{
return s->a;
}
int findb(struct Asd *s)
{
return s->b;
}
int findab(struct Asd *s)
{
return (s->a)+(s->b);
}
struct Asd_option qwe_option =
{
finda,
findb,
findab
};//函数指针赋值
struct Asd Asd_create(int x, int y)//Asd类的构造函数
{
struct Asd asd;
asd.a = x;
asd.b = y;
asd.a_option = &qwe_option;
return asd;
}
int main()
{
Asd a = Asd_create(1,2);
int x = a.a_option->get_a(&a);
int y = a.a_option->get_b(&a);
int z = a.a_option->ab(&a);
printf("X:[%d] Y:[%d] Z:[%d]\n", x,y,z);
printf("Asd_a:[%d]\n", a.a);
return 0;
}
在C语言中,结构体就能完成简单的封装。因为结构体中是不能像C++类那样声明函数成员,所以要使用函数指针完成这一部分的功能。
struct Asd_option qwe_option =
{
finda,
findb,
findab
};
这就是通过结构体对函数指针赋值。这样使得Asd类既有数据成员,又有函数成员。
上面的例子完成了最简单的封装,而且数据成员都是公有的,接下来实现保护或私有数据成员,类似于:
class Asd
{
private:
int x,y;
public:
int a,b;
int findx(){return x;}
int findy(){return y;}
int xy(){return x+y;}
};
#include
#include
struct Asd;
struct Asd_option//可以设为保护成员也可以设为私有成员
{
int x;
int y;
int (*get_x)(struct Asd*);
int (*get_y)(struct Asd*);
int (*xy)(struct Asd*);
};
typedef struct Asd//Asd类
{
int a;
int b;
struct Asd_option *a_option;//函数指针
}Asd;
int findx(struct Asd *s)//具体操作
{
return s->a_option->x;
}
int findy(struct Asd *s)
{
return s->a_option->y;
}
int findxy(struct Asd *s)
{
return (s->a_option->x)+(s->a_option->y);
}
struct Asd_option qwe_option =
{
0,
0,
findx,
findy,
findxy
};//函数指针赋值
Asd Asd_create(int m, int n)//Asd类的构造函数
{
struct Asd asd;
asd.a = 3;
asd.b = 4;
asd.a_option = &qwe_option;
asd.a_option->x = m;
asd.a_option->y = n;
return asd;
}
int main()
{
Asd a = Asd_create(1,2);
int x = a.a_option->get_x(&a);
int y = a.a_option->get_y(&a);
int z = a.a_option->xy(&a);
printf("X:[%d] Y:[%d] Z:[%d]\n", x,y,z);
printf("Asd_a:[%d]\n", a.a);
printf("Asd_b:[%d]\n", a.b);
//printf("Asd_x:[%d]\n", a.x);
//printf("Asd_y:[%d]\n", a.y); //x,y保护(或私有)数据成员,不能直接访问
return 0;
}
接下来实现简单的重载多态:
#include
#include
typedef struct intasd
{
int a;
int b;
}Int_asd;
typedef struct floatasd
{
float x;
float y;
}Float_asd;
typedef void* (*Addfunction)(void*); //typedef定义函数指针的语法:typedef 返回类型 (*指针名)(参数表)
void* intadd(void *asd)
{
Int_asd *s = (Int_asd*)asd;
int amount = s->a + s->b;
printf("Result: = [%d]\n", amount) ;
}
void* floatadd(void *asd)
{
Float_asd *s = (Float_asd*)asd;
float amount = s->x + s->y;
printf("Result: = [%f]\n", amount) ;
}
void* Add(Addfunction f, void *asd)
{
return f(asd);
}
int main()
{
Int_asd x = {1, 2};
Float_asd y = {1.1, 2.2};
Add(intadd, &x);
Add(floatadd, &y);
return 0;
}
实现继承和包含多态:
#include
#include
#include
struct Shape;
struct Shape_option
{
float (*area)(struct Shape*);/*返回几何体的面积*/
int (*perimeter)(struct Shape*);/*返回几何体的周长*/
};
typedef struct Shape //基类
{
char* Shape_name;
struct Shape_option *Shape_ops; /*虚函数,功能由子类实现*/
}Shape;
float Shape_area(struct Shape* s)/*求形状面积*/
{
return s->Shape_ops->area(s);
}
int Shape_perimeter(struct Shape* s) /*求周长*/
{
return s->Shape_ops->perimeter(s);
}
typedef struct triangle/*三角形类*/
{
Shape Base;//公有继承
int a;
int b;
int c;
}Triangle;
float Triangle_area(struct Shape* s)/*三角形面积,用海伦公式*/
{
Triangle *t = (Triangle*)s;
int x = t->a;
int y = t->b;
int z = t->c;
float p = (x+y+z)/2;
return sqrt(p*(p-x)*(p-y)*(p-z));
}
int Triangle_perimeter(struct Shape* s)/*三角形周长*/
{
Triangle* t = (Triangle*)s;
int x = t->a;
int y = t->b;
int z = t->c;
return x+y+z;
}
struct Shape_option triangle_ops =/*对父类虚函数的实现*/
{
Triangle_area,
Triangle_perimeter
};
Triangle* Triangle_create(int x,int y,int z)/*三角形构造函数*/
{
Triangle* ret = (Triangle*)malloc(sizeof (*ret));
ret->Base.Shape_name = "triangle";
ret->Base.Shape_ops = &triangle_ops;
ret->a = x;
ret->b = y;
ret->c = z;
return ret;
}
typedef struct rectangle/*矩形类*/
{
Shape Base;//公有继承
int width;
int height;
}Rectangle;
float Rectangle_area(struct Shape* s)/*矩形函数成员*/
{
Rectangle* r = (Rectangle*)s;
return r->width * r->height;
}
int Rectangle_perimeter(struct Shape* s)/*矩形函数成员*/
{
Rectangle* r = (Rectangle*)s;
return (r->width+r->height)*2;
}
struct Shape_option rectangle_ops =/*对父类虚函数的实现*/
{
Rectangle_area,
Rectangle_perimeter
};
Rectangle* Rectangle_create(int width, int height)/*矩形构造函数*/
{
Rectangle* ret = (Rectangle*)malloc(sizeof(*ret));
ret->Base.Shape_name = "rectangle";
ret->Base.Shape_ops = &rectangle_ops;
ret->height = height;
ret->width = width;
return ret;
}
int main()
{
Shape *s[4];
s[0] = (Shape*)Triangle_create(5,5,4);
s[1] = (Shape*)Triangle_create(3,4,5);
s[2] = (Shape*)Rectangle_create(10,12);
s[3] = (Shape*)Rectangle_create(5,8);
int i=0;
for(i=0 ;i<4 ;i++)
{
float area = Shape_area(s[i]);
int perimeter = Shape_perimeter(s[i]);
char *name = s[i]->Shape_name;
printf("name:%s ,area:%.2f ,perimeter:%d\n",name,area,perimeter);
}
return 0;
}
Shape是基类,Triangle类继承基类Shape,并新增数据成员a、b、c,实现简单的继承。上述例子中,子类对父类虚函数进行实现,实现了包含重载。虽然上面的结果可能不能像C++所展示的那样,但是思想上是面向对象的。
运算符重载将重载的概念扩展到运算符上,允许赋予C++运算符多种含义。实际上,C++(包括C语言)运算符已经被重载。例如,将*运算符用于地址,将得到存储在这个地址中的值;但将它用于两个数字时,得到的将是他们的乘积。
参考:http://blog.chinaunix.net/uid-26921272-id-3360269.html