OOP in C, implementation and a bug

2019-06-04 16:11发布

问题:

I am trying to explore OOP in C. I am however a C n00b and would like to pick the brilliant brains of stackoverflow :)

My code is below:

#include <stdio.h>
#include <stdlib.h>

typedef struct speaker {
    void (*say)(char *msg);
} speaker;

void say(char *dest) {
  printf("%s",dest);
}

speaker* NewSpeaker() {
  speaker *s;
  s->say = say;
  return s;
}

int main() {
  speaker *s = NewSpeaker();
  s->say("works");
}

However I'm getting a segfault from this, if I however remove all args from say and make it void, I can get it to work properly. What is wrong with my current code?

Also. While this implements a form of object in C, I'm trying to further implement it with inheritance, and even overriding/overloading of methods. How do you think I can implement such?

Thank You!

回答1:

In your code, NewSpeaker() doesn't actually create a "new" speaker. You need to use a memory allocation function such as malloc or calloc.

speaker* NewSpeaker() {
   speaker *s = malloc(sizeof(speaker));
   s->say = say;
   return s;
}

Without assigning the value from, for example, the return value of malloc, s is initialized to junk on the stack, hence the segfault.



回答2:

Firstly, as it has been noted already, you failed to allocate memory for your 'speaker' object in 'NewSpeaker'. Without the unnecessary clutter it would look as follows

speaker* NewSpeaker(void) 
{
  speaker *s = malloc(sizeof *s);   
  s->say = say;   
  return s;
}

Note, that there's no cast on the result of the malloc, no type name in the 'sizeof' argument and the function parameter list is declared as '(void)', not just '()'.

Secondly, if you want to be able to create non-dynamic objects of your 'speaker' type, you might want to provide an in-place initialization function first, and then proceed from there

speaker* InitSpeaker(speaker* s)
{
  assert(s != NULL);
  s->say = say;
  return s;
}

speaker* NewSpeaker(void)
{
  void *raw = malloc(sizeof(speaker));
  return raw != NULL ? InitSpeaker(raw) : NULL;
}

Finally, if you really want to create something like virtual C++ methods, you need to supply each method with a 'this' parameter (to get access to other members of your object). So it should probably look something like

typedef struct speaker 
{    
  void (*say)(struct speaker *this, char *msg);
} speaker;

void say(speaker *this, char *dest) 
{  
  printf("%s",dest);
}

This, of course, will require you to pass the corresponding argument every time you call a "method", but there's no way around this.

Additionally, I hope you know that you need "method" pointers in your "class" for "virtual methods" only. Ordinary (non-virtual) methods don't need such pointers.

Finally, a "traditional" C++ class imlementation doesn't store virtual method pointers inside each instance of the class. Instead, they are placed in a separate table (VMT), pointer to which is added to each instance. This saves a lot of memory. And this, BTW, makes especially good sense when you implement inheritance.



回答3:

You can implement inheritance by embedding the parent class structure in the top of the child class structure. That way you can safely cast from the child class to the parent class. Here's an article on implementing OO features in C. If you want an existing solution, or just want to learn more about ways of achieving OO, look at the GObject library.



标签: c oop