You are right that I should have added by default.
>The way this is often implemented is by having the addresses of the various vtables for different ancestors stored in the object data, and the conversion from the descendant class to one of the ancestor class returns an interior pointer to one of these vtable locations inside the object data.
Suggests that a pointer value would still exist that would reference the beginning of the allocation block, stored in the vtable so it would still avoid erroneous collection.
Edit: but you are right that there are some valid reasons to have an interior pointer, which is why the GC has the friendly macros.
No; the vtable normally contains a read-only list of member function pointers, shared across all objects of that type. Objects need to be kept alive solely through the interior pointer to the (compiler-implemented) field (itself a pointer to a vtable) inside the object.
C++ terms:
class C : public A, public B {};
Typical class layout, in C terms:
struct A
{
// exists assuming virtual methods on A
// the data this vtable points to is read-only but C++ ctor semantics requires
// updating vtable pointer during inherited constructor calls
A_vtable *vtable;
// A data
};
struct B
{
B_vtable *vtable;
};
struct C
{
C_vtable *vtable; // incorporates A's vtable
// struct A a; data members of A but without vtable
struct B b;
};
A pointer to an instance of C, but typed as B * , will point at the structure contained in C; it will be an interior pointer. There's no reason to expect that there will be another live pointer of type C * pointing to the object; a pointer of type C * needs to be sufficient. Typecasting back down the hierarchy to C * again (with dynamic_cast) will need to adjust the pointer to the start of the object; this normally requires RTTI, and may be done by e.g. storing the offset of the vtable inside the object in the vtable itself (perhaps at a negative offset).
The above is all implementation dependent, of course, but what I've described above maps almost exactly to how Delphi implements COM interfaces (where A would be the base class and B would be a pure virtual class, or interface - no data members).
>The way this is often implemented is by having the addresses of the various vtables for different ancestors stored in the object data, and the conversion from the descendant class to one of the ancestor class returns an interior pointer to one of these vtable locations inside the object data.
Suggests that a pointer value would still exist that would reference the beginning of the allocation block, stored in the vtable so it would still avoid erroneous collection.
Edit: but you are right that there are some valid reasons to have an interior pointer, which is why the GC has the friendly macros.