C语言面向对象之继承、多态、可变参数、函数指针

来源:互联网 发布:电脑设计软件培训 编辑:程序博客网 时间:2024/05/29 14:01

最近要交一个小作业:用C语言实现类的封装、继承和多态,就顺手写一写吧。第一次写博客,若有错误之处,还望指教。内容有:
1. 类的定义(私有变量)
2. 类的方法(函数指针,构造/析构函数)
3. 类的继承(基类/派生类)
4. 多态(可变参数)

1. 类的定义

C++中用class来定义类,C语言可用struct结构体来表示类,使用另一个结构体变量来作为私有成员变量,使用函数指针作为类的方法。

// 作为私有变量的结构体struct PrivatePoint{    int x;    int y;};// Point类typedef struct point{    struct PrivatePoint priPoint;}Point;

你或许也会发现,这样定义之后还是可以像point->priPoint.x这样直接访问私有变量x,这点和C++的私有的概念不同。我的理解是,这个“私有”的意思是,你可以将PrivatePoint的定义放在另一个.h文件中,而在Point类定义之处只需声明struct PrivatePoint; 没人知道该私有变量里面的内容。

2. 函数指针

函数指针是指向函数的指针变量,其声明格式为:

返回值类型 ( * 指针变量名) ([形参列表]);

在结构体中定义一个函数指针变量,表示类的方法;该方法在相应的.c文件中实现。此处以Shape类为例,后面会讲到Triangle类(三角形)和Circle类(圆形)作为其继承类。

// CClassBase.r#ifndef _CCLASSBASE_R_#define _CCLASSBASE_R_#include <stdarg.h>// 基类:形状,该类作为虚类typedef struct{    int size;   // 结构体大小    // 构造函数    void* (*constructor)(const void *self, va_list *va);        // 析构函数    void* (*destructor)(void *self);        // 求面积函数                     double (*area)(const void *self);}Shape;#endif   

3. 继承

上述shape类作为基类,由于是虚类,其构造/析构函数并未实现,系统并不会实例化一个shape类对象。Triangle类、Circle类作为其继承类,当系统创建一个继承类的对象时,会调用相应的构造函数。

首先,定义继承类Triangle、Circle。

// CClassChildren.r#ifndef _CLASSCHILDREN_R_#define _CLASSCHILDREN_R_#include "CClassBase.r"// 三角形类,继承于形状类typedef struct{    const Shape shape;    double sideLen;}Triangle;// 圆形类,继承于形状类typedef struct{    const Shape shape;    double radius;}Circle;#endif

然后,实现基类的方法。

// CClassChildren.c#include "CClassBase.r"#include "CClassChildren.r"#include <math.h>#define PI 3.1415926//*****************************  Triangle  *******************************static void* Triangle_constructor(const void* _self, va_list* arg_ptr)  {                   // Triangle类的构造函数    Triangle* self = (Triangle*)_self;    self->sideLen = va_arg(*arg_ptr, double);    return self;}static void* Triangle_destructor(void* _self){                   // Triangle类的析构函数    return 0;}static double Triangle_area(const void* _self){                   // Triangle类的求面积函数    Triangle* self = (Triangle*)_self;    double len = self->sideLen;    return sqrt(3.0) / 4 * len * len;}// 声明一个Triangle变量static const Shape _Triangle = { sizeof(Triangle), Triangle_constructor, Triangle_destructor, Triangle_area };const void* triangle = &_Triangle;//*****************************  Circle  ************************************static void* Circle_constructor(const void* _self, va_list* arg_ptr)        {               // Circle类的构造函数    Circle* self = (Circle*)_self;    self->radius = va_arg(*arg_ptr, double);    return self;}static void* Circle_destructor(void* _self){               // Circle类的析构函数    return 0;}static double Circle_area(const void* _self){               // Circle类的求面积函数    Circle* self = (Circle*)_self;    double r = self->radius;    return PI * r * r;}// 声明一个Circle变量static const Shape _Circle = { sizeof(Circle), Circle_constructor, Circle_destructor, Circle_area };const void* circle = &_Circle;

最后,在.h文件中声明继承类变量。上面的代码实现相当于对用户隐藏。

// CClassChildren.h#ifndef _CCLASSCHILDREN_H_#define _CCLASSCHILDREN_H_extern const void* triangle;extern const void* circle;#endif

看完这些之后会不会一头雾水呢?基类和继承类都定义好了,现在该考虑如何创建类的对象并调用其方法了。我们不妨想想C++是如何实现这些步骤的:首先new一个类对象 –> 调用该类的构造函数 –> 通过调用类的方法实现相应功能 –> 调用类的析构函数,销毁该类对象。

// CClassNew.hifndef _CCLASSNEW_H_#define _CCLASSNEW_H_#include <malloc.h>`#include <stdarg.h>#include <assert.h>#include "CClassBase.r"// 创建一个形状的类void* new_shape(const void* _self, ...){    const Shape* shape = (const Shape*)_self;    void* p = calloc(1, shape->size);    *(const Shape**)p = shape;    if (shape->constructor) // 若定义了构造函数,则传参    {        va_list va;        va_start(va, _self);        p = shape->constructor(p, &va);        va_end(va);    }    return p;}// 销毁类void delete_shape(void* _self){    const Shape** shape = (const Shape**)_self;    if (_self && shape && (*shape)->destructor)        _self = (*shape)->destructor(_self);    free(_self);}// 求形状的面积double area(const void* _self){    const Shape* const *p = (Shape**)_self;    assert(_self && *p && (*p)->area);    return (*p)->area(p);}#endif

这里的参数_self是指传进来的不同继承类,当调用该方法的时候,会根据不同的继承类来调用相应的类的方法,从而实现多态

4. 多态

上面的new_shape、delete_shape、area函数根据不同的类调用不同的方法,就是实现多态。在程序中的具体调用如下:

// main.c#include <stdio.h>#include <stdlib.h>#include "CClassChildren.h"#include "CClassNew.h"int main(){    double a = 2.0, b = 1.0, c = 3.0;    void * p = new_shape(triangle, a);    printf("边长为 %.1f 的等边三角形面积为: %f\n\n", a, area(p));    delete_shape(p);    p = new_shape(circle, b);    printf("半径为 %.1f 的圆形面积为: %f\n\n", b, area(p));    delete_shape(p);    return 0;}

这里补充一下可变参数的用法。说到可变参数,最常见的一个例子就是printf,其定义为int printf( const char* format, …); 省略号表示参数的个数不定。由此,一个具有可变参数的函数的定义格式为:

函数返回值 函数名(第一个参数, ...);

使用可变参数会用到以下宏,头文件为

 void va_start( va_list arg_ptr, prev_param );  // 读取开始 type va_arg( va_list arg_ptr, type );     // 读取下一个参数 void va_end( va_list arg_ptr );           // 读取结束

这里的va_arg调用一次就读取下一个参数,无法计数。

// 例子void func(int param, ...){    va_list arg_ptr;    va_start(arg_ptr, param);    int i = va_arg(arg_ptr, int); // 读取第二个参数    int j = va_arg(arg_ptr, int); // 读取第三个参数    va_end(arg_ptr);    printf("%d %d", i, j);}

参考如下:
http://blog.csdn.net/foruok/article/details/18192167
C语言中可变参数的用法

0 0
原创粉丝点击