虚函数备忘

把关于虚函数不清楚的地方小测试了下,记录下来,备忘

测试编译器:mingw32-gcc-3.4.5

 

 

struct A{
virtual void f1(){
std::cout
<<"A::f1"<<std::endl;
}
virtual void f2(){
std::cout
<<"A::f2"<<std::endl;
}
void f3(){
std::cout
<<"A::f3"<<std::endl;
}
};

struct B:A{
void f1(){
std::cout
<<"B::f1"<<std::endl;
}
virtual void f2(){
std::cout
<<"B::f2"<<std::endl;
}
void f3(int){
std::cout
<<"B::f3"<<std::endl;
}
};

void vf_test(){

A a;
B b;

//now, p1 point to a B instance, "*p1" object's vpointer point to B's vtable
A *p1=new B;

//ok, p1 is A's pointer, so it calls none virtual function A::f3()
p1->f3();
//compile time error, notice that the member functions have the same name as the derived-class's will be rewrite.
//so, B::f3(int) hides A::f3()
//b.f3();

//calls A::f1()
(*p1).A::f1();
//calls B::f1()
(*p1).f1();
//calls B::f2()
(*p1).f2();

//(A(*p1)).f1() and (A(*p1)).f2() symbols will be linked to A's member function.
(A(*p1)).f1();
(A(
*p1)).f2();
//but, notice that "*p1" object's vpointer point to B's vtable
//so, after type casting, "*p1" object's vpointer looks like already point to A's vtable at runtime
//let's take a test about it
//A's and B's object only have a vpointer of 4 bytes
std::cout<<"size of A is "<<sizeof(A)<<std::endl;
std::cout
<<"size of B is "<<sizeof(B)<<std::endl;

std::cout
<<"virtual pointer's value of type A is "<<*(unsigned long*)(&a)<<std::endl;
std::cout
<<"virtual pointer's value of type B is "<<*(unsigned long*)(&b)<<std::endl;
std::cout
<<"virtual pointer's value of p1 is "<<*(unsigned long*)p1<<std::endl;
std::cout
<<"virtual pointer's value of &A(*p1) is "<<*(unsigned long*)(&(A(*p1)))<<std::endl;

delete p1;
}

 

output:

A::f3
A::f1
B::f1
B::f2
A::f1
A::f2
size of A is 4
size of B is 4
virtual pointer's value of type A is 4508528
virtual pointer's value of type B is 4508544
virtual pointer's value of p1 is 4508544
virtual pointer's value of &A(*p1) is 4508528

 

这个测试主要是说明虚表指针的运行时切换问题,关于虚函数与类的作用域的较详细解释可参见《C++ Primer 4th Edition》15.5.4, 特别要注意派生类成员名字屏蔽基类成员名字的问题,这里只摘录其中一段对于继承体系中名字查找的小结供大家参考:

  • 关键概念:名字查找与继承;理解c++中继承的关键在于理解如何确定函数调用。确定函数调用遵循以下四个步骤:

    1. 首先确定进行函数调用的对象、引用或指针的静态类型。
    2. 在该类中查找函数,如果找不到,就在直接基类中查找,如此循着类的继承链往上找,直到找到该函数或者查找完最后一个类。如果不能在类或其相关基类中找到该名字,则调用是错误的。
    3. 一旦找到了该名字,就进行常规类型检查,查看如果给定找到的定义,该函数调用是否合法。
    4. 假定函数调用合法,编译器就生产代码。如果函数是虚函数且通过引用或指针调用,则编译器生成代码以确定根据对象的动态类型运行哪个函数版本,否则,编译器生成代码直接调用函数。
  • posted @ 2010-05-15 14:51  tnt_vampire  阅读(277)  评论(0编辑  收藏  举报