C語言-----如何面向對象編程

1、引語

  • 編程語言發展到今天,已經有相當多種了,有些甚至都沒有聽說過和接觸過。對於一個程序員來說,C語言都是基礎,C語言是一個面向過程的程序設計語言,那麼如何使用C語言來寫出面向對象的程序呢?

  • 面向對象編程的三個基本特徵是封裝、繼承和多態。

  • 先使用一個C++的例子,然後把例子轉成C語言。

////////////////////////////Object.h/////////////////////////

//基類
class Object
{
public:
    Object(double h);
    ~Object(){}
    virtual double calcuArae() = 0; //計算表面積
    virtual double calcuVolume() = 0;//計算體積
    //有一個共同的屬性
    double height;
};

//圓柱體
class Cylinder :public Object
{
public:
    Cylinder(double h,double r);
    ~Cylinder(){}
    virtual double calcuArae();
    virtual double calcuVolume();
    double radius;
};

//長方體
class Cuboid :public Object
{
public:
    Cuboid(double h,double w,double l);
    ~Cuboid(){}
    virtual double calcuArae();
    virtual double calcuVolume();
    double weith;
    double length;
};

實現:

///////////////////////////Object.cpp///////////////////////
//基類構造函數
Object::Object(double h)
    :height(h)
{
}
/////////////////////////////////////////////////////////
Cylinder::Cylinder(double h, double r)
    :Object(h)
    ,radius(r)
{

}
double Cylinder::calcuArae()
{
    return 2*PI*radius*radius + 2*PI*radius*height;
}
double Cylinder::calcuVolume()
{
    return  (PI*(radius*radius)/4)*height;
}
/////////////////////////////////////////////////////////
Cuboid::Cuboid(double h, double w, double l)
    :Object(h)
    ,weith(w)
    ,length(l)
{
}
double Cuboid::calcuArae()
{
    return length*weith*height;
}
double Cuboid::calcuVolume()
{
    return 2*(length*weith+weith*height+length*height);
}

多態:

    QVector<Object*> vec;
    vec<< new Cylinder(2,3);
    vec<< new Cuboid(2,3,4);

    for(int i = 0 ; i < vec.size();i++)
    {
        qDebug()<<vec.at(i)->calcuArae();
        qDebug()<<vec.at(i)->calcuVolume();
    }

C++的多態是依靠虛函數來實現的。

2、封裝

在C++中使用class來封裝數據與方法的 在C語言中使用的struct來封裝數據與方法的,在標準的C運行庫裏一組函數fopen(), fclose(),fread(), and fwrite() ,就是在操作FILE結構的,每個函數都會傳入一個FILE的指針。

封裝比較好辦

//抽象對象
typedef struct 
{
    int16_t x;  
    int16_t y;  
} Shape;

//構造函數
Shape *Shape_new(int16_t x, int16_t y)
{
    Shape * obj = (Shape*)malloc(sizeof(Shape));
    obj->x = x;
    obj->y = y;
    return obj;
}
//方法
void Shape_moveBy(Shape * const me, int16_t dx, int16_t dy)
{
    me->x += dx;
    me->y += dy;
}

3、繼承

C語言裏的繼承有點像C++的組合,子類包函父類的對象。

//具體對象
typedef struct 
{
    Shape super; //這裏一定要放在最前面
    uint16_t width;
    uint16_t height;
} Rectangle;

//構造函數
Rectangle*Rectangle_new(int16_t x, int16_t y,uint16_t width, uint16_t height)
{
    Rectangle* r = (Rectangle*)malloc(sizeof(Rectangle));
    r->super.x = x;
    r->super.y = y;
    r->width = width;
    r->height = height;
    return r;
}
//方法,使用基類的方法

應用:

Rectangle* r1 = Rectangle_new(1,1,2,3);
Shape_moveBy((Shape *)r1, -2, 3);//向上類型轉換,upcasting在C++裏是安全的

4、多態

  • 多態的實現就是建立一張虛表,然後實現後綁定。對於C語言來說就是使用函數指針的方法。

先封裝:

//基類
typedef struct
{
    struct ObjectVtbl const *vptr;//虛表,這個也得放在最前面
    double height;
}Object;

//虛表類
struct ObjectVtbl
{
    double (*area)(Object * const o);
    double (*volume)(Object * const o);
};

//Cylinder子類
typedef struct
{
    Object super;
    double radius;
}Cylinder;

//Cuboid子類
typedef struct
{
    Object super;
    double width;
    double length;
}Cuboid;

再把所有的方法實現了:
這些方法只在模塊內使用不expouse給用戶

static double cylinder_area(Object * const o)
{
    Cylinder * const me = (Cylinder *)o;

    double r = (uint)me->radius;
    double h = (uint)o->height;

    return 2*PI*r*r + 2*PI*r*h;
}
static double cylinder_volume(Object * const o)
{
    Cylinder * const me = (Cylinder *)o;

    double r = (uint)me->radius;
    double h = (uint)o->height;

    return  (PI*(r*r)/4)*h;
}
/////////////////////////////////////////////////////////////
static double cuboid_volume(Object * const o)
{
    Cuboid * const me = (Cuboid *)o;

    double w = (uint)me->width;
    double h = (uint)o->height;
    double l = (uint)me->length;

    return l*w*h;
}

static double cuboid_area(Object * const o)
{
    Cuboid * const me = (Cuboid *)o;

    double w = (uint)me->width;
    double h = (uint)o->height;
    double l = (uint)me->length;

    return 2*(l*w+w*h+l*h);
}

再把構造函數給實現了:

Cuboid * new_Cuboid(double l,double w,double h)
{
    //這裏要使用static、const,因爲所有的實例都使用同一套函數
    //而且這裏應該要在rom上分配空間
    static struct ObjectVtbl const vtbl = { &cuboid_area,
                                            &cuboid_volume,
                                            };
    Cuboid * c = (Cuboid *)malloc(sizeof(Cuboid));
    c->super.vptr = &vtbl;
    c->super.height = h;
    c->length = l;
    c->width = w;

    return c;
}

Cylinder * new_Cylinder(double r,double h)
{
    static struct ObjectVtbl const vtbl = { &cylinder_area,
                                            &cylinder_volume,
                                            };
    Cylinder * c = (Cylinder *)malloc(sizeof(Cylinder));
    c->super.vptr = &vtbl;
    c->super.height = h;

    c->radius = r;

    return c;
}

最後把要後綁定的方法實現了:


static double calcu_area(Object * const o)
{
    return (*o->vptr->area)(o);
}

static double calcu_volume(Object * const o)
{
    return (*o->vptr->volume)(o);
}

使用:

    Cylinder *cylinder = new_Cylinder(2,6);
    Cuboid *cuboid = new_Cuboid(2,6,5);

    Object *table[2];
    table[0] = (Object *)cylinder;
    table[1] = (Object *)cuboid;

    for(int i = 0 ; i < 2 ;i++)
    {
        qDebug()<<calcu_area(table[i]);
        qDebug()<<calcu_volume(table[i]);
    }

5、總結

  1. Object Oriented Programming 是一種設計方法而非一種語言
  2. 封裝與繼承,C語言可以輕鬆的handle,但是多態的話相對複雜,而且沒有帶來任何性能上的提升。
  3. 如果是寫框架之類的可以考慮使用多態來隱藏細節,一般的應用不要使用。

6、參考

https://www.cs.rit.edu/~ats/books/ooc.pdf
https://www.state-machine.com/doc/AN_OOP_in_C.pdf
https://www.youtube.com/watch?v=d5k_HBPFm0M
https://stackoverflow.com/questions/415452/object-orientation-in-c
http://ldeniau.web.cern.ch/ldeniau/html/oopc.html
https://sourceforge.net/projects/lwoopc/

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章