Re: Interview questions

From: Niklas Borson (niklasb_at_microsoft.com)
Date: 06/25/04


Date: 24 Jun 2004 23:37:27 -0700


"E. Robert Tisdale" <E.Robert.Tisdale@jpl.nasa.gov> wrote in message news:<cbfgl4$p68$1@nntp1.jpl.nasa.gov>...
> Niklas Borson wrote:
> > E. Robert Tisdale wrote:
[snip]
> >>You need to be able to destroy const objects
> >>as well as variable objects with the same destructor!
> >
> > Yes, if you want to following the example of C++ but,
> > of course, if you're reinventing the wheel,
> > you have the option of changing the rules. :-)
>
> But you don't have the option of failing to destroy const objects.
> They may contain pointers to memory allocated dynamically
> and you will need to free that memory unless you want memory leaks.

I'll go along with that. I was thinking of the argument that
comes up from time to time of whether it should be possible
to delete an object through a pointer-to-const. It is possible,
of course, but some people think it shouldn't be.

> >>
> >> // Actually, the struct Foo_VTable definition
> >> // belongs here and *not* in the public header file.
> >
> > No, because the user of the class needs to know the layout
> > of the vtable structure in order to call pseudo-virtual
> > functions (see example below).
>
> No. The user should *not* call the functions out of the vtable
> directly but implement *virtual* functions in foo.c or bar.c
> that do so.

If I understand correctly, you're saying the users of the
class should call a wrapper function which in turn calls
the actual virtual function through the pointer in the
vtable. Yes, one could do this.

Personally, I don't see the advantage of this extra
function call. IMO, exposing the structure of the vtable
does not violate encapsulation. It doesn't expose the
class's data, for example. It tells you only what virtual
functions the class has -- i.e., its interface.

Moreover, anyone who wants to derive from the class
needs to know the structure of the vtable anyway so
as to provide a compatible vtable for the derived
class.

> >>Now suppose that you want to *derive* Bar from Foo
> >
> > Then Bar would need to have
> > its own Bar_VTable structure definition
> > which would have the same members as Foo_VTable
> > plus possibly some additional members.
>
> Which my example shows:
>
> // implementation
> #include "bar.h"
>
> struct Bar_VTable {
> void (*dtor)(const Bar*);
> int (*f)(Bar*, int, int);
> double (*g)(Bar const*, int);
> char* hello(const Bar*);
> } Bar_VTable;
>
> const Bar_VTable g_Bar_vtable = {
> Bar_dtor,
> Bar_f,
> Bar_g,
> Bar_hello
> };

Yes, and note that this means the implementor of Bar needs to
know the structure of Foo_VTable because the members they have
in common have to have the same layout. This to me suggests
that the structure of the vtable is part of the interface of
a class -- at least for derivers of the class, so why not for
users of the class as well. (Of course if you're using C++
the structure of the vtable is compiler-dependant, but it
is still *implied* by the class definition, and is visible to
other C++ code that uses the class.)

As an aside, note that the virtual methods themselves need to
be visible directly (not just through pointers) for a couple
of reasons. First, Bar_dtor should call Foo_dtor.

Second, Bar might want to inherit some virtual methods of Foo.
For example, to inherit f, we'd set the second member of
g_Bar_vtable to Foo_f instead of Bar_f.

> > It too would have to be public and each instance of Bar
> > would point to a static const instance of Bar_VTable.
>
> No!
> Bar is *derived* from Foo.
> An object of type Bar *is* an object of type Foo.
> There is no special syntax in C to express this
> so you *must* write:
>
> typedef struct Bar {
> Foo foo;
> // and, possibly, other data members
> } Bar;

The above is the layout we'd use if we wanted to follow the
typicall C++ approach. The constructor for Bar would be
implemented something like this:

void Bar_ctor(Bar* bar)
{
    // Initialize the foo "base class"; as per C++ rules,
    // the virtual function pointer will point to the base
    // class vtable during base class initialization.
    Foo_ctor(&bar->foo);
    
    // Now change the virtual function pointer to point to
    // the "derived" class vtable. This requires that the
    // vtables have a compatible layout.
    bar->foo->_vptr = &g_Bar_vtable;
    
    // Perform other initialization here.
}

Because of the compatible vtable layouts, and because Foo
is the first member of bar, we can say a Bar "is a" Foo.
Of course, a C compiler doesn't know this so a cast is
requird to convert a Bar* to a Foo*.

> An object of type Bar *encapsulates* an object of type Foo.
> If, insteas, you write
>
> typedef struct Bar {
> Foo foo;
> const Bar_VTable* _vptr;
> // and, possibly, other data members
> } Bar;
>
> You cannot pass a pointer to an object of type Bar
> to a function of a pointer to an object of type Foo
> and expect it to find pointer member _vptr.

Of course not, and I never suggested using the above
representation.

> The user should *not* call the functions
> out of the virtual function table directly.
> Instead, the class library developer should provide
> *virtual* functions which do this for the user
>
> int Foo_virtual_f(Foo* _this, int i, int j) {
> return ((const Foo_VTable*)(_this->_vptr))->f(_this, i, j);
> }
>
> for example.
> That way, the actual representation of the virtual
> function table remains private to the implementation.

Well, not exactly private. More like "protected" as
implementors of derived classes still need to know
the vtable layout.

Like I said above, you could this. I see nothing wrong
with providing wrapper functions like the above. But
I don't agree that exposing the structure of a vtable
violates encapsulation. It exposes only the set of
virtual functions a class provides -- which is part
of its interface -- not any of the class's actual
data.



Relevant Pages

  • accessor/mutator - design flaw
    ... consider the class BAR which has a member data in_use that FOO ... Similarily FOO has member data 'idx' that BAR ... int in_use; ...
    (comp.lang.cpp)
  • Re: whats a callback?
    ... Rene may have ... 'foo' needs to be written now, which has to call another function ... 'bar' that may not exist yet. ... So you declare the interface signature of 'bar', and make a pointer to ...
    (comp.arch.embedded)
  • Re: whats a callback?
    ... Rene may have ... 'foo' needs to be written now, which has to call another function ... 'bar' that may not exist yet. ... So you declare the interface signature of 'bar', and make a pointer to ...
    (sci.electronics.design)
  • Re: "Mastering C Pointers"....
    ... > Windows shortcut, there is definitely an analogy there. ... a pointer variable holds an address of an object. ... int foo = 12; ... int *bar; ...
    (comp.lang.c)
  • Re: Why pointer to member function?
    ... > binary memory model of a PTMF is a function pointer. ... You are missing the fact that in general case the situation with member ... Both calls to 'foo' and consequent indirect calls to member functions ... the correction depends on the memory layout of the class (which is ...
    (comp.lang.cpp)