C++][基礎知識5]虚関数

4532 ワード

五虚関数
VC++の虚関数は主にプログラム実行の多態性を実現するために用いられる.マルチステートとは、関数の呼び出しなどの同じメッセージが異なるタイプのオブジェクトに受け入れられると、異なる動作を引き起こすことを意味します.マルチステート性により、ユーザは、一般的な形式のメッセージを送信するだけで、メッセージを受信するオブジェクトにすべてのインプリメンテーションを残す.オブジェクトは、受信したメッセージに基づいて対応する操作を行います.
マルチステート性はオブジェクト向けプログラム設計の重要な特徴の一つである.前述したパッケージング性と継承性とがオブジェクト向けプログラム設計の3つの特徴を構成している.この3つの特性は相互に関連している.パッケージ性は基礎であり,継承性は鍵であり,多態性は補完である.
VC++のマルチステートは,コンパイル時のマルチステートと実行時のマルチステートの2つに分けられる.コンパイル時のマルチステート性は主に関数のリロードと演算子のリロードによって実現され、プログラムが実行される前に(コンパイル時に)その実現方法を決定することができる.実行時のマルチステート性は、主に虚関数によって実現されるプログラムの実行時まで待たなければならない.
虚関数の定義と使用
関数を虚関数として定義すると、関数の具体的な実装が不確定であることを示し、同じ呼び出し形式は実行時の異なるオブジェクトに基づいて実装方法を決定します.関数のリロードとは異なり、関数のリロードはその呼び出し形式(関数のパラメータ)に基づいてその実現方式を決定することができる.
クラスで、ある関数の前にキーワード「virtual」を付けると、その関数を虚関数として説明することを示します.虚関数は必ずクラスのメンバー関数であり、その多態性はクラスの継承関係によって実現される.クラス内の関数を虚関数として説明すると、クラスの派生クラスの同じプロトタイプの関数(同じ名前で、パラメータタイプ、順序、個数が同じ)は、キーの有無にかかわらず虚特性を有します.例:

#include<iostream.h>
class A{
public:
	virtual void fun()        //A 
	{cout<<" A    
";} }; class B:public A{ public: void fun() {cout<<" B
";} }; void main() { A a1,*p; B b1; p=&a1; p->fun(); //B p=&b1; p->fun(); //C }

プログラムの実行結果:
クラスAの関数
クラスBの関数
プログラムベースクラスのA行が関数fun()を虚関数として説明すると、クラスAの派生クラスBでは関数fun()は「virtual」で修飾されていないが、虚関数でもあり、主関数のB行とC行では呼び出し形式は同じであるが、2回の呼び出しでポインタpが指すオブジェクトが異なるため、異なるオブジェクトのメンバー関数が呼び出されるため、その出力も異なる.
虚関数の説明と使用には、次の点に注意してください.
(1)虚関数は異なる継承関係のあるクラスの同じプロトタイプ関数(同名でパラメータタイプ、順序および個数が同じ)と同じであり、関数名が同じだけが関数のリロードに属する.個々の虚関数には実際の意味はありません.
(2)虚関数の多態性は同じポインタで実現されなければならず,オブジェクト名で虚関数を呼び出すことで虚関数の多態性を体現できない.一般に,虚関数を実現する多態性は,ベースクラスのポインタが異なるオブジェクトを指すことによって虚関数を処理する.
(3)虚関数はクラスのメンバ関数であるが,静的メンバ関数ではなく,さらに友元関数ではない.友元関数はクラスのメンバーではありません.
(4)派生クラスに虚関数が再定義されていない場合,虚関数の多態性は現れない.
純虚関数
ベースクラスの関数の機能が一時的に特定できないか、または事前に決定する必要がない場合は、派生クラスで定義されるように、この関数を具体的に定義するのではなく、純粋な虚関数として説明することができます.純粋な虚関数の定義は、関数の原型を明確にした虚関数を初期値0に割り当てることである.例:

#include<iostream.h>
class A{
protected:
	int a;
public:
	A(int x){a=x;}
	virtual void fun(int)=0;  //A 
};
class B:public A{
	int b;
public:
	B(int x,int y):A(y){b=x;}
	void fun(int x)     //B 
	{
		cout<<a+x<<'
'; cout<<b+x<<'
'; } }; void main() { A *p; //C B b1(2,6); p=&b1; p->fun(1); //D }

プログラムは、基底クラスAにおいて、関数void fun(int)を純虚関数(A行)として定義する.ベースクラスAには純虚関数が含まれているため、純虚関数は具体的に実装されていないため、オブジェクトを生成するために利用することはできないが、プログラム内のC行などのクラスを指すポインタを定義することができる.
純粋な虚関数を定義します.本質的には空のポインタ値0を関数名に割り当てることですが、この関数のプロトタイプは、純粋な虚関数が空の関数とは異なり、空の関数には空の関数体があり、何もしませんが、完全な関数であり、呼び出すことができます.
プログラム中の派生クラスBのメンバvoid fun(int)はクラスAにおける純虚関数の実装であり,その関数プロトタイプは上記純虚関数と同じでなければならない.
プライマリ関数にベースクラスを指すポインタが定義され、派生クラスのオブジェクトに割り当てられた互換性ルールに従って指定され、Bクラスの関数funが呼び出されます.これは実行中のマルチステートです.pは派生クラスのオブジェクトを指すが、形式的にはクラスAを指すため、クラスAに対応する関数の説明がなければ、プログラム内のD行コンパイル時にエラーが発生し、実行時にのみオブジェクトb 1を指すと判断される.したがって,Aの関数funは文法的には必須であるが,機能的には必須ではなく,その関数の実装を定義する必要がないため,シリアルダミー関数として定義される.
(一)

#include<iostream.h>
class A{
	int a,b;
public:
	int sum(int x,int y)
	{return (x+y);}
};
class B:public A{
public:
	int sum(int x,int y)
	{return (x*y);}
};
void main()
{
	A a1,*p;
	B b1;
	p=&a1;
	cout<<p->sum(3,2)<<'
'; p=&b1; cout<<p->sum(3,2)<<'
'; cout<<a1.sum(3,2)<<'
'; cout<<b1.sum(3,2)<<'
'; }

実行結果:5 5 5 5 6
(二)

#include<iostream.h>
class A{
	int a,b;
public:
	virtual int sum(int x,int y)
	{return (x+y);}
};
class B:public A{
public:
	int sum(int x,int y)
	{return (x*y);}
};
void main()
{
	A a1,*p;
	B b1;
	p=&a1;
	cout<<p->sum(3,2)<<'
'; p=&b1; cout<<p->sum(3,2)<<'
'; cout<<a1.sum(3,2)<<'
'; cout<<b1.sum(3,2)<<'
'; }

実行結果:5 6 5 6