1. Welcome to our site! Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.
    Dismiss Notice

Object-oriented style objects in C

Discussion in 'Code Repository' started by misterT, Nov 5, 2013.

  1. misterT

    misterT Well-Known Member Most Helpful Member

    Joined:
    Apr 19, 2010
    Messages:
    2,681
    Likes:
    361
    Location:
    Finland
    Simplified version how to do object-oriented style objects in C (Polymorphism). (adapted from the book "Understanding and using C pointers")

    Because memory for variables in structures are always allocated in the same order they are declared, you can easily define a base-structure for your objects. A base structure for a shape object could be simply coordinates:

    Code (C):

    struct shape {
        int x;
        int y;
    };
     
    Now you can define couple of different shapes that "inherit" from the basic shape-struct. Just remember that the order is important. Always put the base structure at the top.

    Code (C):

    struct rectangle {
        struct shape base;
        int width;
        int height;
    };

    struct circle {
        struct shape base;
        int radius;
    };
     

    Now, if you want to have a function that moves a shape to given coordinates, you do not have to write a function for every shape you have. You can write a function that works with the base-structure only.

    Code (C):

    // Move the shape to coordinates x, y
    void move_shape(struct shape* obj, int x, int y)
    {
        obj->x = x;
        obj->y = y
    }
     
    this is how you would use it:

    Code (C):

    // Declare and init couple of shapes
    // A Rectangle
    struct rectangle rect;
    rect.x=0;
    rect.y=5;
    rect.width = 10;
    rect.height = 3;

    // A Circle
    struct circle circ;
    circ.x = 120;
    circ.y = 30;
    circ.radius = 5;

    // Move the circle to origin
    move_shape((struct shape*)&circ, 0, 0);

    // Move the rectangle to 10, 12
    move_shape((struct shape*)&rect, 10, 12);
     
     
    Last edited: Nov 22, 2013
    • Like Like x 2
  2. misterT

    misterT Well-Known Member Most Helpful Member

    Joined:
    Apr 19, 2010
    Messages:
    2,681
    Likes:
    361
    Location:
    Finland
    The next step to polymorphism is to add "methods", or functions that act to the specific object. This step also requires us to add a "constructor" -function, which in C is a function that allocate memory for the structure and initializes all necessary variables.

    Let's say we want to add a "ToString" -function to our shape-object. The function should print out the type of shape our object is. If it is a rectangle, the function prints "Rectangle".. etc.

    It is not possible to write functions inside structures, but what can be done is to store function pointers inside the structures (all pointers are the same size, no matter where, or to what, they point).

    This is how a function pointer is declared:
    Code (C):

    // step 1) The name of the variable is "toString"
    toString;

    // 2) To make this a pointer, add the '*', the parenthesis are needed to make sure that things are declared in the right order.
    (*toString); // Now the variable is a pointer.

    // 3) To make it a function pointer we add parenthesis to denote a function call (void). The "void" tells the compiler that the function does not take in any parameters.
    (*toString)(void);

    // 4) Last thing to add is the return value of the function. Since our function does not return anything, the final form of the function-pointer is
    void (*toString)(void);
     
    Now, we can update the base structure to hold this function pointer. We add it as the first variable. (Because usually there is a list of functions (methods), and it is good practice to make the list as the first element)

    Our structures are:
    Code (C):

    struct shape {
        void (*toString)(void);
        int x;
        int y;
    };

    struct rectangle {
        struct shape base;
        int width;
        int height;
    };

    struct circle {
        struct shape base;
        int radius;
    };
     
    Now we need to write the "toString" functions for each object. There is no way around it.. well, you could have the same function for all of them, but that is not very usefull. When inheriting from some base "class" you usually override some functions. This means writing a function specific for that object.

    Here are the functions:
    Code (C):

    // toString for shape -base structure
    void toStringShape(void){
        print("Shape"); // we assume that there is some print-function for us.
    }

    // toString for rectangle
    void toStringRectangle(void){
        print("Rectangle");
    }

    // toString for circle
    void toStringCircle(void){
        print("Circle");
    }
     

    Constructing these objects is now more complicated since we added object-specific methods. We need Constructors to put them together for us.
    Code (C):

    // Constructor for rectangle object. It allocates memory for the structure and returns a pointer to it.
    struct rectangle* new_rectangle() {
        struct rectangle* rect = (struct rectangle*)malloc(sizeof(struct rectangle)); // allocate memory for the structure
        rect->base.toString = toStringRectangle; // assign the address of the function "toStringRectangle()" to the "toString" function pointer
        rect->base.x = 10;
        rect->base.y = 15;
        rect->width = 40;
        rect->height = 60;
        return rect;
    }

    // Constructor for circle object.
    struct circle* new_circle() {
        struct circle* circ = (struct circle*)malloc(sizeof(struct circle));
        circ->base.toString = toStringCircle;
        circ->base.x = 0;
        circ->base.y = 0;
        circ->radius = 5;
        return circ;
    }
     
    This all may seem complicated, but when we put this all to use, the benefits are clear (hopefully)

    Lets declare an array of shapes:
    Code (C):

    struct shape* shapes[5];

    shapes[0] = new_circle();
    shapes[1] = new_rectangle();
    shapes[2] = new_rectangle();
    shapes[3] = new_circle();
    shapes[4] = new_rectangle();

    // Now, we can "print" all shapes using a for-loop
    for(int i=0; i<5; i++) {
       shapes[i]->toString();
    }

    /* This should print out:
    Circle
    Rectangle
    Rectangle
    Circle
    Rectangle
    */

     
     
    Last edited: Nov 22, 2013
    • Like Like x 4
  3. misterT

    misterT Well-Known Member Most Helpful Member

    Joined:
    Apr 19, 2010
    Messages:
    2,681
    Likes:
    361
    Location:
    Finland
  4. dave

    Dave New Member

    Joined:
    Jan 12, 1997
    Messages:
    -
    Likes:
    0


     
  5. killivolt

    killivolt Well-Known Member

    Joined:
    Mar 12, 2008
    Messages:
    3,211
    Likes:
    121
    Location:
    U.S.

    Strike this question.
    Edit: I figured it out.


    Thanks, kv
     
    Last edited: Apr 14, 2014
  6. NorthGuy

    NorthGuy Well-Known Member

    Joined:
    Sep 8, 2013
    Messages:
    1,214
    Likes:
    206
    Location:
    Northern Canada

Share This Page