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(); //
这样,对于虚函数表就有了一的初步认识!


 

版权声明:本文博客原创文章。博客,未经同意,不得转载。