Reputation: 345
#include<iostream>
using namespace std;
class X
{
int a;
int b;
public:
void f(int a)
{
cout<<"\nInside X";
}
virtual void abc ()
{
cout<<"\nHello X";
}
};
class Y : public X
{
int a;
public:
void f(int a, int b)
{
cout<<"\nInside Y";
}
void abc()
{
cout<<"\nHello Y";
}
};
int main()
{
X a;
cout<<sizeof(X);
Y b;
cout<<sizeof(Y);
X *h = new Y;
h->abc();
}
I understand that the reason the size of class X is 12 bytes, is because it contains a vptr (virtual pointer) to the virtual table. Is there anyway I could read this virtual table and if not, could at least access the virtual pointer. I tried using unions, but it gave me some kind of error.
Also, when I call h->abc() , how does it know the object of class, h
is pointing to? I thought most of this was done at compile time. But when you have a base class pointer pointing to a derived class, how does it know which class function to execute.
Consider these two situations
X *h = new X;
h->abc();/* This would call the abc function in X */
and
X *h = new Y;
h->abc();/* This would call the abc function in Y*/
I read, that the h
pointer would go to the vtable of the object it is pointing to and would hence call that function? But how is this achieved in runtime?
Upvotes: 3
Views: 410
Reputation: 1216
Ok you first question:I give you a example which may be better understanding!
#include<iostream>
using namespace std;
class Base1 {
public:
int ibase1;
Base1():ibase1(10) {}
virtual void f() { cout << "Base1::f()" << endl; }
virtual void g() { cout << "Base1::g()" << endl; }
virtual void h() { cout << "Base1::h()" << endl; }
};
class Base2 {
public:
int ibase2;
Base2():ibase2(20) {}
virtual void f() { cout << "Base2::f()" << endl; }
virtual void g() { cout << "Base2::g()" << endl; }
virtual void h() { cout << "Base2::h()" << endl; }
};
class Base3 {
public:
int ibase3;
Base3():ibase3(30) {}
virtual void f() { cout << "Base3::f()" << endl; }
virtual void g() { cout << "Base3::g()" << endl; }
virtual void h() { cout << "Base3::h()" << endl; }
};
class Derive : public Base1, public Base2, public Base3 {
public:
int iderive;
Derive():iderive(100) {}
virtual void f() { cout << "Derive::f()" << endl; }
virtual void g1() { cout << "Derive::g1()" << endl; }
};
This the memory rank of a derived class which implemented three base classes base1, base2, base3, where you :
Base1 *p1 = new Derive();
Base2 *p2 = new Derive();
Base3 *p3 = new Derive();
p1 will point the vtale1, p2 will point to vtable2, p3 will point to vtable3, If you call some virtual function it will find the very virtual table and get the address!
In your code:
X *h = new Y;
h will point to beginning position of the memory of Y, which is the virtual table of X,he will find the address of abc()
which implemented in Y!
your second question:
The complier will consider the member function as the normal function, so it put the address of member function in the code section
,so it does not occupied the memory!!
If you want to read the virtual table you can try like this:which is I tried in my example in gcc4.7
typedef void(*Func)(void);
Derive d;
int **pd = (int **)(&d);
int i = 0;
while(i < 4)
{
Func f = (Func)pd[0][i];
f();
i++;
}
int s = (int)(pd[1]);
cout << s << endl;
i = 0;
cout << "===============================================" << endl;
while(i < 3)
{
Func f = (Func)pd[2][i];
f();
i++;
}
s = (int)(pd[3]);
cout << s << endl;
cout << "===============================================" << endl;
i = 0;
while(i < 3)
{
Func f = (Func)pd[4][i];
f();
i++;
}
s = (int)(pd[5]);
cout << s << endl;
s = (int)(pd[6]);
cout << s << endl;
and you will get the result as follow:
Derive::f()
Base1::g()
Base1::h()
Derive::g1()
10
===============================================
Derive::f()
Base2::g()
Base2::h()
20
===============================================
Derive::f()
Base3::g()
Base3::h()
30
100
Upvotes: 6
Reputation: 137930
You shouldn't try to access the vtable pointer unless you're sure what you're doing. In terms of the language, which is usually what we use to define the meaning of a program, the vtable doesn't even exist. It's an implementation detail, and it belongs to the implementation (a.k.a. the compiler and the runtime environment).
If the implementation is constrained by a portable ABI (application binary interface), then the ABI will say where to find the vtable pointer and what's inside the vtable. reinterpret_cast< vtable const * const & >( my_obj )
should do the trick to get the pointer from an object on any "reasonable" ABI.
Such a program would be constrained to one platform and one ABI. (C++ ABI interfaces tend to change more often than C but less often than other languages.) Depending on the ABI is a poor design choice, unless you're just trying to prove you're crazy.
The vtable identifies the derived class — that is its purpose. It contains pointers to functions implemented by the derived class overriding the base. It also contains a structure with the name of the derived class and links to its bases, so as to dynamically determine what it is derived from.
The algorithm used by dynamic_cast
to determine derivation and find a derived object may actually be surprisingly slow — not O(1). It usually has to search a linked structure of base classes.
Upvotes: 2
Reputation: 311039
Is there anyway I could read this virtual table
Not really, not without knowing where the pointer is with respect to the object pointer value, which is compiler-specific.
and if not, could at least access the virtual pointer.
Why? You can get the address of the function via h->abc
, is that what you want?
Also, when I call h->abc() , how does it know the object of class, h is pointing to?
It doesn't really, it just knows where the vtable for that class is. If you use RTTI there is information in the vtable to tell it what class, but it isn't necessary for calling virtual functions. Each class derived from X will have its own vtable containing its own pointer(s) for its own virtual functions. (Always assuming a vtable-based implementation of course.)
I read, that the h pointer would go to the vtable of the object it is pointing to and would hence call that function? But how is this achieved in runtime?
You've just described it yourself. To elaborate only slightly, the pointer h->abc
resolves to h->_vtable[x]
for some constant x
representing the offset in the vtable of the virtual function pointer for abc
. So the call resolves to *(h->_vtable[abc])(...)
.
Another question, unrelated to virtual function which I needed to clear up. If function have addresses like any other variable, why don't they occupy space in objects?
Why should they? That would imply a copy of each function in every object. What would be the point?
Upvotes: 1