Continue to Site

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.

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

Object-oriented style objects in C

Status
Not open for further replies.

misterT

Well-Known Member
Most Helpful Member
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:

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.

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.

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:

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:
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:
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:
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:
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.
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:
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:
Strike this question.
Edit: I figured it out.


Thanks, kv
 
Last edited:
Status
Not open for further replies.

Latest threads

New Articles From Microcontroller Tips

Back
Top