#include<iostream>
#include <string>
class Base {
public:
virtual void sayHello() { std::cout << "Hello from base" << std::endl; }
virtual void sayHi() { std::cout << "Hi from base" << std::endl; }
virtual void sayHappy() { std::cout << "Happy from base" << std::endl; }
std::string isbn() const;
//virtual double net_price(std::size_t n) const;
};
class Derived : public Base {
public:
//implictly overide corresponding virtual functions of base class, though not explicitly them as virtual functions
void sayHappy() { std::cout << "Happy from derived class" << std::endl; }
void sayHello() { std::cout << "Hello from derived class" << std::endl; }
void sayHi() { std::cout << "Hi from derived class" << std::endl; }
};
int main() {
typedef void (**ppf)(void);
typedef void(*pf)(void);
Base *qt, aa;
Derived dd;
dd.sayHello();
qt = ⅆ
qt->sayHello();
//get address of the vftable
int *vTableAddress = (int*)(&dd);
//vTable is the pointer to the first element of the vftable
ppf vTable = ((ppf)(*vTableAddress));
//thses are pointers in the vftable
pf say_hello = *vTable;
pf say_hi = *(vTable + 1);
pf say_happy = *(vTable + 2);
for (int i = 0; i < 10; i++) {
std::cout << *(vTable + i) << std::endl;
}
//依次调用 sayHello(), sayHi(), sayHappy()
say_hello();
say_hi();
say_happy();
return 0;
}
通过以上代码实验得到的结论是:
Base *p;
Derived d;
p = &d;
p.vfunc();
通过指向基类的指针来调用虚函数时的顺序是,
在编译期间“:
在基类非虚函数中找要调用的函数,如果找到的话,则调用的就是该方法,不再继续找了;
如果在非虚函数中没有找到的话,则在基类的虚函数表找,如果找到的话,记录该函数在虚函数表中的偏移量,则运行时实际调用的就是刚才找到的那个虚函数对应于指针p指向的那个对象的虚函数,即,只有等到runtime时才能知道调用的是那个虚函数;
如果都没找的话,就在编译时出错。
在新标准下,子类函数中函数名和基类虚函数同名的函数如果不声明virtual,编译器默认把他们当作虚函数对待,同时子类虚函数在虚函数表中的顺序与基类中虚函数表中的相应虚函数的顺序一致。这是因为直接用在编译期间找到那个偏移量加上运行时实际调用那个虚函数表的地址,得到的结果就是指向实际要调用的那个虚函数的指针。