class A
{
public:
virtual void f(){}
}
class B
{
public:
virtual void g(){}
}
class A:public A,public B
{
public:
virtual void h(){}
virtual void f(){}
virtual void g(){}
}
很简单吧,一个多重继承的例子
C类对象布局,最上面的是 A的子对象,含有一个vptr ,紧接着是 B的子对象 还有一个vptr
C * pc = new C();
B * pb = pc;
pc指向的是C对象的首地址,会经过调整后赋值给pb,让pb指向的是 C对象中B的子对象首地址
pb->g();
pb通过vptr扎到虚函数表第一位置的函数,该函数为子类C的g函数,运行时调用,实现多态
现在有个疑问
pc->g();
pc的地址就是C对象的首地址,它对应的vptr是
你看看下面的描述 http://en.wikipedia.org/wiki/Virtual_method_table
Consider the following class declarations in C++ syntax:
class B1
{
public:
void f0() {}
virtual void f1() {}
int int_in_b1;
};
class B2
{
public:
virtual void f2() {}
int int_in_b2;
};
used to derive the following class:
class D : public B1, public B2
{
public:
void d() {}
void f2() {} // override B2::f2()
int int_in_d;
};
and the following piece of C++ code:
B2 *b2 = new B2();
D *d = new D();
G++ 3.4.6 from GCC produces the following 32 bit memory layout for the object b2
:[nb 1]
b2:
+0: pointer to virtual method table of B2
+4: value of int_in_b2
virtual method table of B2:
+0: B2::f2()
and the following memory layout for the object d
:
d:
+0: pointer to virtual method table of D (for B1)
+4: value of int_in_b1
+8: pointer to virtual method table of D (for B2)
+12: value of int_in_b2
+16: value of int_in_d
Total size: 20 Bytes.
virtual method table of D (for B1):
+0: B1::f1() // B1::f1() is not overridden
virtual method table of D (for B2):
+0: D::f2() // B2::f2() is overridden by D::f2()
Note that those functions not carrying the keyword virtual
in their declaration (such as f0()
and d()
) do not generally appear in the vtable. There are exceptions for special cases as posed by the default constructor.
Overriding of the method f2()
in class D
is implemented by duplicating the virtual method table of B2
and replacing the pointer to B2::f2()
with a pointer to D::f2()
.
The G++ compiler implements the multiple inheritance of the classes B1
and B2
in class D
using two virtual method tables, one for each base class. (There are
other ways to implement multiple inheritance, but this is the most
common.) This leads to the necessity for "pointer fixups" (thunks) when casting.
Consider the following C++ code:
D *d = new D();
B1 *b1 = static_cast<B1*>(d);
B2 *b2 = static_cast<B2*>(d);
While d
and b1
will point to the same memory location after execution of this code, b2
will point to the location d+8
(eight bytes beyond the memory location of d
). Thus, b2
points to the region within d
which "looks like" an instance of B2
, i.e., has the same memory layout as an instance of B2
.
还真不好跟你解释,因为你拿结果来质疑流程。
你再仔细看下子类 C 的虚函数表,你会发现你在子类 C 中定义的虚函数 h() 也不见了吧!那 pc->h() 这是如何检索到的呢?
你能由 pb 看到那个虚函数表,是因为你的 pb 实际上是 pc 的地址空间的虚函数表,如果你这样:
B* pb =new B();
你再看 pb 的虚函数表,你就会发现 g() 没了。
虚函数表中始终记录的是父类的虚函数,如果子类重载了父类的虚函数,在父类的虚函数表中被重载的那个虚函数的地址就会被子类的函数的地址替代。
g()在B中声明为虚函数,所以B的空间中会有一个虚函数表,里面记录着g()的地址,对B来说,这个个地址就指向g()函数在B中的实现。
C继承自B,也继承了B的这个虚函数表,里面也记录着g()的地址,只是对C来说,这个地址指向的是g()函数在C中的实现。
pc->g()就是这样找到g()的。