C++の中の多態と虚関数の大きい総括

5573 ワード

マルチステートはC++の重要な部分であり、オブジェクト向けのプログラム設計における役割は特に際立っており、その意味は多様な形式や形態を有する場合であり、簡単に言えば、マルチステート:異なるオブジェクトに同じメッセージを送信し、異なるオブジェクトは受信時に異なる動作を生じる.すなわち、1つの関数名で異なるコンテンツの関数を呼び出すことができる.
マルチステートは静的マルチステートと動的マルチステートに分けることができ、静的マルチステートの実装は静的アセンブリにあり、関連付けは実行期間ではなくコンパイル段階に現れ、呼び出す関数をオブジェクト名またはクラス名で限定し、静的関連付けまたは静的アセンブリと呼ぶ.よくある方法は3つあります
(1)関数マルチステート(関数と演算子のリロード);
(2)マクロマルチステート;
(3)テンプレート多態.
動的マルチステートの実装は,実行段階で虚関数とクラスオブジェクトを結合する,すなわち動的結合,動的結合である.具体的には、ベースクラスを指すポインタで虚メンバー関数を呼び出すと、実行時システムはポインタが指す実際のオブジェクトに基づいて適切なメンバー関数を呼び出すことができる.
コンパイラがダイナミックバインドを使用すると、実行時にオブジェクトのタイプと正しい呼び出し関数が決定されます.コンパイラに遅いバインドを使用させるには、ベースクラスで関数を宣言するときにvirtualキーワードを使用します.このような関数を虚関数(virtual functions)と呼びます.付与互換性に基づいて、ベースクラスタイプのポインタで派生クラスを指すと、このポインタで派生クラスのメンバー関数を使用できます.この関数が通常のメンバー関数である場合、ベースクラスタイプのポインタでアクセスできるのは、ベースクラスの同名メンバーのみです.虚関数に設定すると、ベースクラスタイプのポインタを使用して、ポインタが指す派生クラスの同名関数にアクセスできます.これにより、ベースクラスタイプのポインタにより、異なる派生クラスに属する異なるオブジェクトに異なる動作を生じさせ、実行プロセスのマルチステートを実現することができる.この例を見てみましょう.
#include 
using namespace std;
class A
{ 
public :  	
	void print( ) {   cout << “A::print”<print( );   
	return 0;
}

出力A::print、Aクラスのprint()関数をvirtualと宣言すると、動的コンパイラの実行結果は:B::printとなります.
注意点1:コンストラクション関数と静的メンバー関数は虚関数ではありません:静的メンバー関数は虚関数ではありません.virtual関数はコンパイラによってthisポインタが提供されているためです.静的メンバー関数にはthisポインタがなく、オブジェクトに制限されていません.コンストラクション関数を虚関数にすることはできません.コンストラクションの場合、オブジェクトはまだ未定の空間なので、コンストラクションが完了した後、オブジェクトだけが具体的なクラスのインスタンスです.
class A
{
public:
	virtual A( ) {};  //error 
};
class B
{
public:
	virtual static void func( ) {};  //error  “virtual”   “static”    
};
int main( )
{       
B b; //
A *a=&b; return 0; }

注意点2:派生クラスオブジェクトのポインタは、上のA*a=&bのようなベースクラスポインタに直接割り当てることができます.*aクラスAのオブジェクトと見なし、そのpublicメンバーにアクセスすることができる.ポインタタイプ変換を強制することで、aをBクラスのポインタに変換することができます:a=&b;aa = static_cast< B * > a.また、ベースクラスへのポインタは、共通の派生オブジェクトを指すことができますが、プライベート派生オブジェクトを指すことはできません.参照についても同じです.
class B
{
public:
	virtual void print() { cout<print();
	B& rb = d;  //    ,    
	rb.print();
	return 0;
}

注意点3:コンストラクション関数でvirtual関数を呼び出し、コンストラクション関数と解析関数で虚関数を呼び出す場合:彼らが呼び出す関数は自分のクラスまたはベースクラスで定義された関数であり、実行時になってから自分の関数を呼び出すか派生クラスの関数を呼び出すかを決定することはありません.
class Transaction
{
public: 
	Transaction( ){  logTransaction( ); }
	virtual void logTransaction( ) = 0;
};
class BuyTransaction: public Transaction
{
public:
	int buyNum;
	virtual void logTransaction( ) {  cout<< "This is a BuyTransaction";  }
};
class SellTransaction: public Transaction
{
public:
	int sellNum;
	virtual void logTransaction( )
	{ 
		cout<< "This is a SellTransaction";
	}
};
int main( ) 
{
	BuyTransaction b;
	SellTransaction s;
}

以上のコードにはエラーメッセージが表示されるはずです.
ベースクラスのTransactionの虚関数logTransactionを次のように変更します.
virtual void logTransaction( )

{

cout<< "This is a Transaction"< 
 

プログラム実行結果:This is a Transaction
                               This is a Transaction
注意点4:一般メンバー関数で虚関数を呼び出し、一般メンバー関数で虚関数を呼び出すと、動的に結合され、マルチステートになります.
#include 
using namespace std;
class Base 
{
public:
	void func1( )  { func2( ); }
	void virtual func2( ) { cout << "Base::func2( )" << endl; }
};
class Derived:public Base 
{
public:
	virtual void func2( ) { cout << "Derived:func2( )" << endl; }
};
int main( )
{
	Derived d;
	Base * pBase = & d;
	pBase->func1( );
	return 0;
}

ベースクラスのfunc 1()は非静的メンバー関数であるため、コンパイラはvoid func 1(){this->func 2()}に相当するthisポインタを追加します.この関数のコードをコンパイルする場合,func 2()は虚関数であり,thisはベースクラスポインタであるため動的結合である.上記のプログラムがfunc 1関数に実行されると、thisポインタはdを指しているので、動的に結合され、Derived::func 2()が呼び出されます.
注意点5:虚関数へのアクセス権、ベースクラス定義のメンバー虚関数がプライベートである場合、どうなるか見てみましょう.
class Base{
private:
	virtual void func( )  { cout << "Base::func( )" << endl; }
};
class Derived : public Base {
public: 
 	virtual void func( ) { cout << "Derived:func( )" << endl; }
};
int main( ) 
{
	Derived d;
	Base *pBase = & d;
	pBase->func( );  //     , private   ( “Base”    )
	return 0;
}

クラスのprivateメンバーについては、そのクラスの関数、その友元関数のみがアクセスでき、他のアクセスはできず、そのクラスのオブジェクトもアクセスできない.だから虚関数でもアクセスできません.でも!派生クラスの虚関数のアクセス性は継承の方式と虚関数のベースクラスでの宣言方式(public、またはprivate)と派生クラス宣言の方式(public継承、privateと宣言したがアクセス可能)とは関係なく、上のpublicをprivateと位置を交換し、プログラムを正常に実行し、Derived:func()を出力します.
注意点6:虚関数と友元、まずコードを見ます
class A;
class B
{
private:
		int x;
		void print() { cout< 
 

プログラム実行結果:99
最初の99から分かるように、AはBの友元クラスであり、Aのすべてのメンバー関数はBの友元関数であり、Bのプライベートメンバー関数にアクセスすることができる.友元クラスAはベースクラスBの一部ではなく,派生クラスDの一部でもない.上記の例から見ると、友元は継承可能であり、ベースクラスの友元関数または友元クラスは派生クラスのプライベートメンバーにアクセスすることができる.しかしpublic継承は「is a」の関係であり、派生クラスオブジェクトはベースクラスオブジェクトと見なすことができる.したがって,前例ではベースクラスの友元が継承されたのではなく,派生クラスがベースクラスとして認識された.2番目の99は、1つのメタクラスの派生クラスを説明し、そのベースクラスインタフェースを通じて、そのベースクラスがメタクラスであるクラスを設定するプライベートメンバー、すなわち1つのクラスのメタクラスの派生クラスにアクセスすることができ、ある意味ではそのメタクラスである.
注意点7:構造関数は通常虚関数です.虚析構関数は、析構時にベースクラスの析構関数のみを呼び出し、派生クラスの析構関数を呼び出さないことを保証し、リソースの正常な解放を保証し、メモリの解放を回避します.クラスがベースクラスとして使用される場合にのみ、構造関数は虚関数として書かれます.
以上は個人のまとめで、不適切な点があれば指摘を歓迎します.