In my experience in object oriented C programming I have seen two ways to implement derived classes.
First Method, have a definition of the parent class as a .h file. Then each class that derives from this class will have do:
File parent_class.h:
int member1;
int member2;
File testing.c:
struct parent_class {
#include "parent_class.h" // must be first in the struct
}
struct my_derived_class {
#include "parent_class.h" // must be first in the struct
int member3;
int member4;
}
Second Method, would do:
File testing.c:
struct parent_class {
int member3;
int member4;
}
struct my_derived_class {
struct parent_class; // must be first in the struct
int member3;
int member4;
}
What is your favorite method of doing derived classes in C ( doesn't have to be what I have done )? and why?
Which method would you prefer, first or second method ( or your own )?
The first method is hideous and it hides important information. I'd never use it or allow it being used. Even using a macro would be better:
But method 2 is much better, for reasons others have pointed out.
The code I worked with used the first method.
The only two reasons I can think of for using the first method is:
I prefer the first method because you can cast the derived class pointer to the parent class without any worries.
Can you do the same thing with the second method? ( It seems like you would have to jump through a hoop or two to get the same end result )
If you can do the same with method 2, then I think both methods would be equal.
I have used method #2 before and found it works quite ok:
free()
on the pointer to the base structure will of course also free up the derived fields, so that isn't an issue...Also, I find accessing base fields something I tend to do in a polymorphic situation: I only care about those fields in methods that care about the base type. Fields in the derived type are used in methods only interested in the derived type.
It's the other way round.
The C standard guarantees that the address of a struct is the address of the first member, so in the second case it is safe to cast a pointer to derived to parent, as the first member of derived is the parent struct, and a the struct as a member as the same layout as the same struct when not a member, so casting a pointer to a derived to parent will always work.
The same is not true for the second case. Two structs with some members defined as the same type may have different padding between those members.
It would be reasonable for a 64 bit bigendian compiler to compile
such that sizeof(A) is a whole multiple of 8 and b is 64 bit aligned, but compile
so that sizeof(B) is a whole multiple of 8, but b is only 32 bit aligned so that it doesn't waste space.
I'm one of the maintainers of a library that uses method 2. Works just as well as method 1, but without any preprocessor trickery. Or it actually works better, since you can have functions that take the base class as argument and you can just cast to the base struct, C guarantees that this works for the first member.
The more interesting question is, how do you do virtual functions? In our case, the struct has pointers to all the functions, and the initialization set them up. It's slightly simpler, but has more space overhead than the "proper way" with a pointer to a shared vtable.
Anyway, I'd prefer to use C++ rather than kludge it with plain C, but politics..
Second option forces you to write very long names like
myobj.parent.grandparent.attribute
, which is ugly. First option is better from syntax point of view, but it is a bit risky to cast child to parent - I'm not sure whether is is guaranteed by standard that different structs will have same offsets for similar members. I guess compiler may use different padding for such structs.There is another option, if you are using GCC - anonymous struct members, which is part of MS extension, so I guess it was originated by some MS compiler and still may be supported by MS.
Declarations look like
In your "constructor" function you need to specify correct function for calculating area and the enjoy the inheritance and polymorphism. The only problem that you always need to pass explicit
this
- you cannot just callshape[i]->area()
.Compile with
gcc -fms-extensions
. I never used it in real-life project but I tested it some time ago and it worked.