您的位置:首页 > 编程语言 > C语言/C++

C++学习笔记27,虚函数的工作原理

2014-06-18 10:35 260 查看
C++规定了虚函数的行为,但是将实现交给了编译器的作者.

通常,编译器处理虚函数的方法是给每一个对象添加一个隐藏成员.隐藏成员中保存了一个指向函数地址数组的指针.

这个数组称为虚函数表(virtual function table,vtbl).虚函数表中存储了为类对象进行声明的虚函数的地址.

例如:基类对象包含一个指针,该指针指向基类的虚函数表.

派生类对象包含一个指针,该指针指向一个独立的虚函数表.如果派生类提供了虚函数的新定义,虚函数表将保存新的函数地址.

如果派生类没有重新定义虚函数,虚函数表将保存原版本的地址.如果派生类定义了新的虚函数,则将该函数地址也添加到虚函数表中!

看下面的例子:

#include <iostream>
#include <string>
using namespace std;
class Animal{
protected:
string name;
public:
Animal(const string &s):name(s){
}
virtual ~Animal(){
}
//非虚函数
void eat()const{
cout<<"Animal eat!"<<endl;
}
//不被重写的虚函数
virtual void run()const{
cout<<"Animal run!"<<endl;
}

//会被重写的虚函数
virtual void speak()const{
cout<<"I'm a Animal!"<<endl;
}
};
class Dog:public Animal{
public:
Dog(const string &s):Animal(s){
}
virtual ~Dog(){
}
//新定义的函数eat,将掩盖旧的版本,非重写(重写是指重写virtual函数)
void eat()const{
cout<<"Dog eat!"<<endl;
}
//重写speak()
virtual void speak()const override{
cout<<"This's a Dog!"<<endl;
}
//新的虚函数
virtual void fun1()const{
}
};
int main(){
Animal a("AnimalOne");
Dog d1("DogOne");

Animal *p1=&a;
Animal *p2=&d1;
p1->speak();
p2->speak();
p1->eat();
p2->eat();	//call Animal::eat()
p1->run();
p2->run();	//call Animal::run()

Animal &r1=a;
Animal &r2=d1;
r1.speak();
r2.speak();
r1.eat();
r2.eat();
r1.run();
r2.run();

return 0;

}


注意eat不是虚函数,不会保存在虚函数表中

//Animal虚函数表
地址:1000 

Animal::run()        4000  

Animal::speak()                  5000

Dog虚函数表  地址:2000

Dog::run()        4000(没有被重写,保存原地址)

Dog::speak()  
7000(重写了,保存新地址)

Dog::fun() 8000(新的虚函数,保存地址)

详细分析下面的代码
p1->speak();
//找到Animal中speak的地址,5000
p2->speak();
//找到Dog中speak的地址,7000,执行代码Dog::speak();
p1->run();
//同上
p2->run();
//

这样,对于虚函数表就有了一个初步的了解了!


                                            
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  c++ 虚函数