C++ 例によるポリモーフィズム

⚡ スマートサマリー

ポリモーフィズム C++ 単一のメンバ関数が、呼び出し元のオブジェクトに応じて異なる動作をするなど、多様な形式をとることを可能にする。これは継承に基づいており、コンパイル時または実行時に解決される2つの形式が存在する。

  • 🔷 定義: ポリモーフィズムとは「多くの形態」を意味し、呼び出し元のオブジェクトに基づいて、あるメンバー関数が異なる動作をすることを可能にする。
  • 🧭 2つのタイプ: C++ コンパイル時(静的)多態性と実行時(動的)多態性を提供します。
  • 🧩 コンパイル時間: 関数オーバーロードと演算子オーバーロードは、コンパイル時に適切な関数を選択します。
  • 🔁 実行時間: 関数オーバーライドと仮想関数は、プログラムの実行中に呼び出しを解決します。
  • ⚙️ 仮想機能: 基底クラスのvirtualキーワードは、基底クラスポインタを介した遅延バインディングを可能にします。
  • 🤖 AI支援: GitHub Copilotや同様のアシスタントは、短いコメントからオーバーロードされた関数、オーバーライドされた関数、仮想関数を生成できます。

C++ ポリモーフィズム

ポリモーフィズムとは何か C++?

In C++ポリモーフィズムは、呼び出し元のオブジェクトに基づいてメンバ関数の動作が異なるようにするものです。ポリモーフィズムはギリシャ語で「多くの形を持つ」という意味です。これは、階層構造がある場合に発生します。 クラス 遺伝によって関連している。

たとえば、関数 makeSound() があるとします。 猫がこの関数を呼び出すと、ニャーという音が鳴ります。 牛が同じ機能を呼び出すと、「モー」という音が鳴ります。

ポリモーフィズム C++ makeSoundの例

関数は XNUMX つありますが、状況が異なれば動作も異なります。 関数にはさまざまな形式があります。 したがって、ポリモーフィズムを達成しました。

基本的な考え方が確立されたので、 C++ この行動を2つの明確なカテゴリーに分類する。

多型の種類

C++ 2 種類のポリモーフィズムをサポートします。

  • コンパイル時のポリモーフィズム、および
  • 実行時のポリモーフィズム。

多型性の種類 C++

最初のカテゴリは、プログラムが実行される前にすべての関数呼び出しを解決します。

コンパイル時のポリモーフィズム

オーバーロードされた関数は、引数の数と型を一致させることで呼び出されます。情報はコンパイル時に存在します。つまり、 C++ コンパイラはコンパイル時に適切な関数を選択します。

コンパイル時のポリモーフィズムは、関数のオーバーロードと演算子のオーバーロードによって実現されます。

関数のオーバーロード

関数のオーバーロードは、名前は似ているが引数が異なる関数が多数ある場合に発生します。 引数は、数または型が異なる場合があります。

#include <iostream> 
using namespace std;

void test(int i) {
	cout << " The int is " << i << endl;
}
void test(double  f) {
	cout << " The float is " << f << endl;
}
void test(char const *ch) {
	cout << " The char* is " << ch << endl;
}

int main() {
	test(5);
	test(5.5);
	test("five");
	return 0;
}

出力:

C++ 関数オーバーロードの例1の出力

コードのスクリーンショットは次のとおりです。

C++ 関数オーバーロードの例1コード

Code 説明:

  1. iostream ヘッダー ファイルをコードに含めます。 その機能を利用できるようになります。
  2. コードに std 名前空間を含めます。 呼び出さなくてもそのクラスを使用できるようになります。
  3. 整数パラメーター i を取る test という名前の関数を作成します。 { は関数テスト本体の始まりを示します。
  4. 上記の関数テストが呼び出された場合に実行されるステートメント。
  5. 上記の機能テストの本体は終了です。
  6. float パラメータ f を取る test という名前の関数を作成します。 { は関数テスト本体の始まりを示します。
  7. 上記の関数テストが呼び出された場合に実行されるステートメント。
  8. 上記の機能テストの本体は終了です。
  9. 文字パラメータ ch を受け取る test という名前の関数を作成します。 { は関数テスト本体の始まりを示します。
  10. 上記の関数テストが呼び出された場合に実行されるステートメント。
  11. 上記の機能テストの本体は終了です。
  12. main() 関数を呼び出します。 { は関数本体の始まりを示します。
  13. 関数 test を呼び出し、引数の値として 5 を渡します。 これにより、整数の引数を受け入れるテスト関数、つまり最初のテスト関数が呼び出されます。
  14. 関数 test を呼び出し、引数の値として 5.5 を渡します。 これにより、float 引数を受け入れるテスト関数、つまり XNUMX 番目のテスト関数が呼び出されます。
  15. 関数 test を呼び出し、引数の値として XNUMX を渡します。 これにより、文字引数を受け入れるテスト関数、つまり XNUMX 番目のテスト関数が呼び出されます。
  16. プログラムが正常に実行された場合、プログラムは値を返す必要があります。
  17. main() 関数の本体の終わり。

同じ名前で引数の種類が異なる XNUMX つの関数があります。 ポリモーフィズムを達成しました。

Operaトールのオーバーロード

In Operatorオーバーロードでは、 C++ オペレータまた、演算子の動作方法も変わります。例えば、+演算子を2つの文字列を連結するように定義できます。これは数値の加算演算子として知られています。定義後、+演算子を整数の間に配置した場合は加算され、文字列の間に配置した場合は連結されます。

#include<iostream> 
using namespace std;

class ComplexNum {
private:
	int real, over;
public:
	ComplexNum(int rl = 0, int ov = 0) {
		real = rl;   
		over = ov; 
	}

	ComplexNum operator + (ComplexNum const &obj) {
		ComplexNum result;
		result.real = real + obj.real;
		result.over = over + obj.over;
		return result;
	}
	void print() { 
		cout << real << " + i" << over << endl; 
	}
};
int main()
{
	ComplexNum c1(10, 2), c2(3, 7);
	ComplexNum c3 = c1+c2;
	c3.print();
}

出力:

C++ 演算子オーバーロードの例2の出力

コードのスクリーンショットは次のとおりです。

C++ 演算子オーバーロードの例2のコード

C++ 演算子オーバーロードの例2の結果

Code 説明:

  1. その機能を使用するには、iostream ヘッダー ファイルをプログラムにインクルードします。
  2. std 名前空間を呼び出さずにそのクラスを使用するには、プログラムに std 名前空間を組み込みます。
  3. ComplexNum という名前のクラスを作成します。{ はクラス本体の始まりを示します。
  4. private アクセス修飾子を使用して、変数をプライベートとしてマークします。これは、変数がクラス内からのみアクセスできることを意味します。
  5. 実数とそれ以上の XNUMX つの整数変数を定義します。
  6. public アクセス修飾子を使用してコンストラクタを public としてマークすると、クラスの外部からもアクセスできるようになります。
  7. クラス コンストラクターを作成し、変数を初期化します。
  8. 変数 real の値を初期化します。
  9. 変数の値を初期化します。
  10. コンストラクター本体の終わり。
  11. + 演算子の意味をオーバーライドする必要があります。
  12. ComplexNum 型のデータ型結果を作成します。
  13. 複素数には + 演算子を使用します。この行は、ある数値の実部を別の数値の実部に追加します。
  14. 複素数には + 演算子を使用します。この行は、ある数値の虚数部を別の数値の虚数部に追加します。
  15. プログラムは、実行が成功すると変数 result の値を返します。
  16. + 演算子の新しい意味、つまりオーバーロードの定義はこれで終わりです。
  17. print() メソッドを呼び出します。
  18. 加算後の新しい複素数をコンソールに出力します。
  19. print()関数の本体の終わり。
  20. ComplexNum クラスの本体の終わり。
  21. main() 関数を呼び出します。
  22. 加算する実数部と複素数部の両方の値を渡します。c1 の最初の部分は c2 の最初の部分、つまり 10+3 に加算されます。c1 の 2 番目の部分は c の 7 番目の部分、つまり XNUMX+XNUMX に加算されます。
  23. オーバーロードされた + 演算子を使用して演算を実行し、結果を変数 c3 に格納します。
  24. 変数 c3 の値をコンソールに出力します。
  25. main() 関数の本体の終わり。

コンパイル時の多態性が早期に決定を下すのに対し、2番目のカテゴリでは実行時まで選択を延期する。

ランタイムポリモーフィズム

これは、オブジェクトのメソッドがコンパイル時ではなく実行時に呼び出されるときに発生します。 実行時のポリモーフィズムは、関数のオーバーライドによって実現されます。 呼び出される関数は実行時に確立されます。

関数のオーバーライド

関数のオーバーライドは、基本クラスの関数に派生クラスで新しい定義が与えられるときに発生します。 この時点で、基本関数はオーバーライドされたと言えます。

具体的な例を挙げますと、以下の通りです。

#include <iostream>
using namespace std;
class Mammal {

public:
	void eat() {

		cout << "Mammals eat...";
	}

};

class Cow: public Mammal {

public:
	void eat() {

		cout << "Cows eat grass...";
	}
};
int main(void) {

	Cow c = Cow();

	c.eat();

	return 0;

}

出力:

C++ 関数の出力オーバーライド

コードのスクリーンショットは次のとおりです。

C++ 関数オーバーライドコード

Code 説明:

  1. その機能を使用するには、iostream ヘッダー ファイルをプログラムにインポートします。
  2. std 名前空間を呼び出さずにそのクラスを使用するには、プログラムに std 名前空間を組み込みます。
  3. Mammal という名前のクラスを作成します。 { はクラス本体の始まりを示します。
  4. public アクセス修飾子を使用して、これから作成する関数をパブリックにアクセスできるように設定します。 このクラスの外部からアクセスできるようになります。
  5. Eat という名前のパブリック関数を作成します。 { は関数本体の始まりを示します。
  6. 関数 Eat() が呼び出されたときに cout 関数に追加されたステートメントを出力します。
  7. 関数eat()の本体の終わり。
  8. 哺乳類クラスの体の端。
  9. Mammal クラスを継承する Cow という名前のクラスを作成します。 Cow は派生クラスであり、Mammal は基本クラスです。 { はこのクラスの始まりを示します。
  10. public アクセス修飾子を使用して、これから作成する関数をパブリックにアクセスできるものとしてマークします。 このクラスの外部からアクセスできるようになります。
  11. 基本クラスで定義された関数 Eat() をオーバーライドします。 { は関数本体の始まりを示します。
  12. この関数が呼び出されたときにコンソールに出力されるステートメント。
  13. 関数 Eat() の本体の終わり。
  14. クラス Cow の本体の終わり。
  15. main() 関数を呼び出します。 { は、この関数の本体の始まりを示します。
  16. c. Cow クラスのインスタンスを作成し、名前を付けます。
  17. Cow クラスで定義された Eat() 関数を呼び出します。
  18. プログラムは正常に完了すると値を返す必要があります。
  19. main() 関数の終わり。

C++ 仮想機能

仮想関数は、実行時ポリモーフィズムを実装するもう一つの方法です。 C++これは、基本クラスで定義され、派生クラスで再定義される特別な関数です。仮想関数を宣言するには、virtual キーワードを使用する必要があります。キーワードは、基本クラスで関数の宣言の前に配置する必要があります。

仮想関数クラスが継承されると、仮想クラスはそのニーズに合わせて仮想関数を再定義します。 例えば:

#include <iostream>  
using namespace std;
class ClassA {
		public:
		virtual void show() {
			cout << "The show() function in base class invoked..." << endl;
		}
	};
	class ClassB :public ClassA {
	public:
		void show() 	{
			cout << "The show() function in derived class invoked...";
		}
	};
	int main() {
		ClassA* a;   
		ClassB b;
		a = &b;
		a->show();      
	}

出力:

C++ 仮想関数の出力

コードのスクリーンショットは次のとおりです。

C++ 仮想関数コード

Code 説明:

  1. iostream ヘッダー ファイルをコードに含めて、その機能を使用します。
  2. std 名前空間をコードに含めると、そのクラスを呼び出さずに使用できます。
  3. ClassA という名前のクラスを作成します。
  4. public アクセス修飾子を使用して、クラス メンバーをパブリックにアクセスできるものとしてマークします。
  5. show() という名前の仮想関数を作成します。 公的な行事となります。
  6. show()が呼び出されたときに印刷されるテキスト。endlは C++ 行の終了を意味するキーワード。マウス カーソルを次の行に移動します。
  7. 仮想関数 show() の本体の終わり。
  8. クラス ClassA の本体の終わり。
  9. クラス ClassA を継承する ClassB という名前の新しいクラスを作成します。 ClassA は基本クラスになり、ClassB は派生クラスになります。
  10. public アクセス修飾子を使用して、クラス メンバーをパブリックにアクセスできるものとしてマークします。
  11. 基本クラスで派生した仮想関数 show() を再定義します。
  12. 派生クラスで定義された show() 関数が呼び出されたときにコンソールに出力されるテキスト。
  13. show() 関数の本体の終わり。
  14. 派生クラス ClassB の本体の終わり。
  15. main() 関数を呼び出します。 プログラム ロジックは本体内に追加する必要があります。
  16. a という名前のポインター変数を作成します。 これは、ClassA という名前のクラスを指します。
  17. ClassB という名前のクラスのインスタンスを作成します。 インスタンスには b という名前が付けられます。
  18. アドレスbに格納されている値を変数aに代入します。
  19. 派生クラスで定義された show() 関数を呼び出します。 遅延バインディングが実装されました。
  20. main() 関数の本体の終わり。

両方のメカニズムを網羅した上で、以下の表ではそれらを並べて比較しています。

コンパイル時のポリモーフィズムと実行時のポリモーフィズム

XNUMX つの主な違いは次のとおりです。

コンパイル時のポリモーフィズム 実行時のポリモーフィズム
これは早期結合または静的多型とも呼ばれます。 これは遅延/動的バインディングまたは動的多型とも呼ばれます。
メソッドはコンパイル時に呼び出されます。 メソッドは実行時に呼び出されます。
関数オーバーロードと演算子オーバーロードを介して実装 メソッドのオーバーライドと仮想関数を介して実装されます。
例: メソッドのオーバーロード。 多くのメソッドは似たような名前を持っていますが、引数の数やタイプが異なります。 例: メソッドのオーバーライド。 多くのメソッドは、同様の名前と同じプロトタイプを持つ場合があります。
メソッド検出がコンパイル時に行われるため、実行速度が向上します。 メソッドの検出が実行時に行われるため、実行速度が遅くなります。
Less コンパイル時にすべてがわかるため、問題解決の柔軟性が提供されます。 メソッドは実行時に検出されるため、複雑な問題を解決するための柔軟性が大幅に向上します。

よくあるご質問

オーバーロードは、同じ名前だが異なるパラメータを持つ複数の関数を定義し、継承なしでコンパイル時に解決します。オーバーライドは、継承された基底クラスの関数を派生クラスで同じシグネチャで再定義し、関数が仮想関数として宣言されたときに実行時に解決します。

純粋仮想関数は、= 0 で宣言され、基底クラスに本体を持ちません。純粋仮想関数を含むクラスはすべて絶対仮想関数になります。tract クラスは直接インスタンス化できず、派生クラスに独自の実装を提供するよう強制します。

クラスが仮想関数を宣言すると、コンパイラは関数ポインタの仮想テーブル(vtable)を作成します。各オブジェクトは、そのクラスのvtableへの隠しポインタを格納しているため、実行時に基底クラスのポインタによって、遅延バインディングを通じて適切なオーバーライドされた関数が呼び出されます。

はい。テンプレートを使用すると、1つの関数またはクラスで複数のデータ型を操作でき、コンパイラはコンパイル時に型固有のコードを生成します。この汎用的なプログラミングは、関数オーバーロードや演算子オーバーロードを補完する静的ポリモーフィズムの一種です。

基底クラスのポインタを介して派生オブジェクトを削除する場合、仮想デストラクタのみが派生クラスのデストラクタの実行を保証します。仮想デストラクタがない場合、クリーンアップが不完全になり、メモリリークが発生します。基底クラスのデストラクタを仮想として宣言することで、破棄処理がポリモーフィックに動作するようになります。

ポリモーフィズムにより、1つのインターフェースで複数のオブジェクト型を扱うことが可能になり、コードの重複を減らし、保守を簡素化できます。また、既存のコードを変更することなく新しい派生クラスを組み込める、柔軟で拡張性の高い設計をサポートし、基底クラスのポインタと参照に基づいた強力なパターンを実現します。

はい。AIコーディングアシスタントは、短いコメントをオーバーロード関数、仮想基底クラス、派生オーバーライドに変換します。繰り返し発生する定型コードを迅速に処理しますが、生成されたコードをコンパイルする前に、仮想キーワード、関数シグネチャ、デストラクタの安全性を検証する必要があります。

Yes. GitHubコパイロット 入力時に仮想関数宣言、派生クラスのオーバーライド、演算子オーバーロードを提案します。継承階層の定型コードを高速化しますが、ビルド前にvirtualキーワード、一致するシグネチャ、および遅延バインディングの正しい動作を確認する必要があります。