C Language--How to Program Object-Oriented

Posted by dantone on Sun, 09 Jun 2019 22:33:35 +0200

1. Quotations

  • Today, there are quite a variety of programming languages, some of which have not even been heard or touched. For a programmer, C language is the foundation, C language is a process-oriented programming language, so how to use C language to write object-oriented programs?

  • The three basic characteristics of object-oriented programming are encapsulation, inheritance and polymorphism.

  • First use a C++ example, then turn the example into C language.

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

//base class
class Object
{
public:
    Object(double h);
    ~Object(){}
    virtual double calcuArae() = 0; //Calculating Surface Area
    virtual double calcuVolume() = 0;//calculate volume
    //Have a common attribute
    double height;
};

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

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

Realization:

///////////////////////////Object.cpp///////////////////////
//Base class constructor
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);
}

Polymorphism:

    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++ polymorphism is realized by virtual functions.

2. Packaging

class is used to encapsulate data and methods in C++ and struct is used to encapsulate data and methods in C language. In standard C runtime, a set of functions fopen(), fclose(),fread(), and fwrite() are used to operate FILE structure, and each function will pass in a FILE pointer.

Encapsulation is better

//Abstract object
typedef struct 
{
    int16_t x;  
    int16_t y;  
} Shape;

//Constructor
Shape *Shape_new(int16_t x, int16_t y)
{
    Shape * obj = (Shape*)malloc(sizeof(Shape));
    obj->x = x;
    obj->y = y;
    return obj;
}
//Method
void Shape_moveBy(Shape * const me, int16_t dx, int16_t dy)
{
    me->x += dx;
    me->y += dy;
}

3. Inheritance

Inheritance in C language is a bit like the combination of C++, subclasses encapsulate the objects of the parent class.

//Specific object
typedef struct 
{
    Shape super; //This must be at the top of the list.
    uint16_t width;
    uint16_t height;
} Rectangle;

//Constructor
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;
}
//Method, using the method of base class

Application:

Rectangle* r1 = Rectangle_new(1,1,2,3);
Shape_moveBy((Shape *)r1, -2, 3);//Upcast type conversion, upcast inC++It's safe inside.

4. Polymorphism

  • The realization of polymorphism is to create a virtual table and then bind it after implementation. For C language, it is the method of using function pointer.

First package:

//base class
typedef struct
{
    struct ObjectVtbl const *vptr;//Fictitious table, this must also be put in the front.
    double height;
}Object;

//Virtual table class
struct ObjectVtbl
{
    double (*area)(Object * const o);
    double (*volume)(Object * const o);
};

//Cylinder subclasses
typedef struct
{
    Object super;
    double radius;
}Cylinder;

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

Then all the methods are realized:
These methods only use non-expouse in modules for users

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);
}

Then the constructor is implemented:

Cuboid * new_Cuboid(double l,double w,double h)
{
    //static and const are used here, because all instances use the same set of functions
    //And here you should allocate space on 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;
}

Finally, the post-binding method is implemented:

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

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

Use:

    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. Summary

  1. Object Oriented Programming is a design method, not a language
  2. Encapsulation and inheritance, C language can handle easily, but polymorphic words are relatively complex, and without any performance improvement.
  3. If you are writing frameworks, you can consider using polymorphism to hide details, but not for general applications.

6. Reference

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/

Topics: C Programming Attribute