Mike wrote:
...the C practice of declaring all variables at the start of the function drives me crazy. Not to mention the /*---*/ comments for everything...
Neither of these issues apply to C99. I happily declare variables at the point of use and use // comments. "const" doesn't seem to be "const" the way I expect it though -- declaring a static array sized with a "const" won't compile in C99, although it does in C++.
I am using virtual functions all the time.
I've come across the need for polymorphism here and there, but I choose to implement it without vtables. From a design perspective, I'm doing everything in a purely interface-based manner -- no inheritance whatsoever. Here's what it looks like in C (note that this is off the top of my head and probably won't compile):
Code: Select all
// IFoo.h
typedef void (* IFoo_helloFunc)( void*, const char* );
typedef struct
{
IFoo_helloFunc hello;
} IFoo_itable;
typedef struct
{
IFoo_itable* iptr;
void* obj;
} IFoo;
...
// MyFoo.h
typedef struct MyFooStruct MyFoo;
void MyFoo_hello( MyFoo* this, const char* msg );
IFoo createFoo( void ); // Factory function.
// MyFoo.c
static IFoo_itable s_MyFoo_itable;
struct MyFooStruct
{
... // Actual fields of MyFoo.
};
void MyFoo_hello( MyFoo* this, const char* msg )
{
...
}
...
// Probably done in some kind of "init" function... Moral
// equivalent of a static constructor in C#...
s_MyFoo_itable.hello = MyFoo_hello;
...
IFoo createFoo( void )
{
MyFoo* myFoo = ... // Get a MyFoo from somewhere...
IFoo foo;
foo.iptr = &s_MyFoo_itable;
foo.obj = myFoo;
return foo;
}
// SomewhereElse.c
// Notice the actual type of foo is unknown, and the behaviour
// is determined by the interface dispatch table (itable).
IFoo foo = createFoo();
foo.iptr->hello( foo.obj, "world!" );
It's quite verbose, but it gets the job done. It has the advantages that:
- the fields of "classes" such as MyFoo are better encapsulated (due to the use of opaque pointers), and
- If you have a "class" that implements multiple interfaces, the size of each object does not grow with additional vptrs, because there are no vptrs.
The obvious disadvantage (besides the syntax) is that "interface references" are twice the size of regular pointers.
It's actually not possible to do this directly in C++, although there was a compiler out there called HeronFront that could do this kind of thing automatically (
http://www.heron-language.com/).