[セットトップ]C++学習ノート27,虚関数の動作原理

2131 ワード

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(新しい虚関数、保存アドレス)
以下のコードp 1->speak();//を詳細に分析するAnimalのspeakのアドレスを見つけて、5000 p 2->speak();//Dogの中のspeakのアドレスを見つけて、7000、コードDogを実行します::speak();p1->run(); //同上p 2->run();//これで、虚関数テーブルについて初歩的な理解が得られました!