Storing type information
You can see that there is no explicit
type information stored in any of the classes. But the previous examples, and
simple logic, tell you that there must be some sort of type information stored
in the objects; otherwise the type could not be established at runtime. This is
true, but the type information is hidden. To see it, here’s an example to
examine the sizes of classes that use virtual functions compared with those that
don’t:
//: C15:Sizes.cpp
// Object sizes with/without virtual functions
#include <iostream>
using namespace std;
class NoVirtual {
int a;
public:
void x() const {}
int i() const { return 1; }
};
class OneVirtual {
int a;
public:
virtual void x() const {}
int i() const { return 1; }
};
class TwoVirtuals {
int a;
public:
virtual void x() const {}
virtual int i() const { return 1; }
};
int main() {
cout << "int: " << sizeof(int) << endl;
cout << "NoVirtual: "
<< sizeof(NoVirtual) << endl;
cout << "void* : " << sizeof(void*) << endl;
cout << "OneVirtual: "
<< sizeof(OneVirtual) << endl;
cout << "TwoVirtuals: "
<< sizeof(TwoVirtuals) << endl;
} ///:~
With no virtual functions, the size of
the object is exactly what you’d expect: the size of a
single[55]
int. With a single virtual function in OneVirtual, the size of the
object is the size of NoVirtual plus the size of a void pointer.
It turns out that the compiler inserts a single pointer (the VPTR) into the
structure if you have one or more virtual functions. There is no size
difference between OneVirtual and TwoVirtuals. That’s
because the VPTR points to a table of function addresses. You need only one
table because all the virtual function addresses are contained in that single
table.
This example required at least one data
member. If there had been no data members, the C++ compiler would have forced
the objects to be a nonzero size
because each object must have a
distinct address. If you imagine indexing into an array of zero-sized objects,
you’ll understand. A “dummy” member is inserted into objects
that would otherwise be zero-sized. When the type information is inserted
because of the virtual keyword, this takes the place of the
“dummy” member. Try commenting out the int a in all the
classes in the example above to see
this.